dicetables 4.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dicetables/__init__.py +13 -0
- dicetables/additiveevents.py +71 -0
- dicetables/dicepool.py +36 -0
- dicetables/dicepool_collection.py +140 -0
- dicetables/dicerecord.py +69 -0
- dicetables/dicetable.py +111 -0
- dicetables/dieevents.py +484 -0
- dicetables/eventsbases/__init__.py +0 -0
- dicetables/eventsbases/eventerrors.py +13 -0
- dicetables/eventsbases/integerevents.py +46 -0
- dicetables/eventsbases/protodie.py +74 -0
- dicetables/eventsinfo.py +289 -0
- dicetables/factory/__init__.py +0 -0
- dicetables/factory/errorhandler.py +112 -0
- dicetables/factory/eventsfactory.py +181 -0
- dicetables/factory/factorytools.py +78 -0
- dicetables/factory/warninghandler.py +60 -0
- dicetables/parser.py +274 -0
- dicetables/roller.py +44 -0
- dicetables/tools/__init__.py +0 -0
- dicetables/tools/alias_table.py +67 -0
- dicetables/tools/dictcombiner.py +210 -0
- dicetables/tools/indexedvalues.py +109 -0
- dicetables/tools/limit_checker.py +205 -0
- dicetables/tools/listtostring.py +47 -0
- dicetables/tools/numberforamtter.py +133 -0
- dicetables/tools/orderedcombinations.py +50 -0
- dicetables-4.0.2.dist-info/LICENSE +21 -0
- dicetables-4.0.2.dist-info/METADATA +277 -0
- dicetables-4.0.2.dist-info/RECORD +49 -0
- dicetables-4.0.2.dist-info/WHEEL +6 -0
- dicetables-4.0.2.dist-info/top_level.txt +2 -0
- tests/eventsbases/__init__.py +0 -0
- tests/eventsbases/test_eventserrors.py +30 -0
- tests/eventsbases/test_integerevents.py +119 -0
- tests/eventsbases/test_protodie.py +189 -0
- tests/factory/__init__.py +0 -0
- tests/factory/test_eventsfactory.py +772 -0
- tests/factory/test_factoryerrorhandler.py +118 -0
- tests/factory/test_factorytools.py +201 -0
- tests/factory/test_factorywarninghandler.py +172 -0
- tests/tools/__init__.py +0 -0
- tests/tools/test_alias_table.py +108 -0
- tests/tools/test_dictcombiner.py +374 -0
- tests/tools/test_indexedvalues.py +168 -0
- tests/tools/test_limit_checker.py +214 -0
- tests/tools/test_listtostring.py +77 -0
- tests/tools/test_numberformatter.py +340 -0
- tests/tools/test_orderedcombinations.py +114 -0
dicetables/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from dicetables.additiveevents import AdditiveEvents
|
|
2
|
+
from dicetables.dicepool_collection import BestOfDicePool, WorstOfDicePool, UpperMidOfDicePool, LowerMidOfDicePool
|
|
3
|
+
from dicetables.dicepool import DicePool
|
|
4
|
+
from dicetables.dicerecord import DiceRecord
|
|
5
|
+
from dicetables.dicetable import DiceTable, DetailedDiceTable
|
|
6
|
+
from dicetables.dieevents import Die, ModDie, WeightedDie, ModWeightedDie, StrongDie, Modifier, ExplodingOn, Exploding
|
|
7
|
+
from dicetables.eventsbases.eventerrors import DiceRecordError, InvalidEventsError
|
|
8
|
+
from dicetables.eventsinfo import (EventsCalculations, EventsInformation,
|
|
9
|
+
events_range, mean, stddev, percentage_points, percentage_axes, stats,
|
|
10
|
+
full_table_string)
|
|
11
|
+
from dicetables.parser import Parser, ParseError
|
|
12
|
+
from dicetables.tools.limit_checker import LimitsError
|
|
13
|
+
from dicetables.roller import Roller
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AdditiveEvents is the general case for DiceTable - an IntegerEvents that can combine with other IntegerEvents.
|
|
3
|
+
"""
|
|
4
|
+
from typing import TypeVar, Type, Dict
|
|
5
|
+
|
|
6
|
+
from dicetables.eventsbases.integerevents import IntegerEvents
|
|
7
|
+
from dicetables.factory.eventsfactory import EventsFactory
|
|
8
|
+
from dicetables.tools.dictcombiner import DictCombiner
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def scrub_zeroes(dictionary):
|
|
12
|
+
return {key: val for key, val in dictionary.items() if val}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
T = TypeVar('T', bound='AdditiveEvents')
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AdditiveEvents(IntegerEvents):
|
|
19
|
+
|
|
20
|
+
def __init__(self, events_dict):
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
:param events_dict: {event: occurrences}\n
|
|
24
|
+
event=int. occurrences=int >=0
|
|
25
|
+
total occurrences > 0
|
|
26
|
+
"""
|
|
27
|
+
self._table = scrub_zeroes(events_dict)
|
|
28
|
+
super(AdditiveEvents, self).__init__()
|
|
29
|
+
EventsFactory.check(self.__class__)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def new(cls: Type[T]) -> T:
|
|
33
|
+
return EventsFactory.new(cls)
|
|
34
|
+
|
|
35
|
+
def get_dict(self) -> Dict[int, int]:
|
|
36
|
+
return self._table.copy()
|
|
37
|
+
|
|
38
|
+
def __str__(self):
|
|
39
|
+
min_event = min(self._table.keys())
|
|
40
|
+
max_event = max(self._table.keys())
|
|
41
|
+
return 'table from {} to {}'.format(min_event, max_event)
|
|
42
|
+
|
|
43
|
+
def combine(self: T, events: IntegerEvents, times: int = 1) -> T:
|
|
44
|
+
dictionary = DictCombiner(self.get_dict()).combine_by_fastest(events.get_dict(), times)
|
|
45
|
+
return EventsFactory.from_dictionary(self, dictionary)
|
|
46
|
+
|
|
47
|
+
def combine_by_flattened_list(self: T, events: IntegerEvents, times: int = 1) -> T:
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
:WARNING - UNSAFE METHOD: len(flattened_list) = total occurrences of events.
|
|
51
|
+
if this list is too big, it will raise MemoryError or OverflowError
|
|
52
|
+
"""
|
|
53
|
+
dictionary = DictCombiner(self.get_dict()).combine_by_flattened_list(events.get_dict(), times)
|
|
54
|
+
return EventsFactory.from_dictionary(self, dictionary)
|
|
55
|
+
|
|
56
|
+
def combine_by_dictionary(self: T, events: IntegerEvents, times: int = 1) -> T:
|
|
57
|
+
dictionary = DictCombiner(self.get_dict()).combine_by_dictionary(events.get_dict(), times)
|
|
58
|
+
return EventsFactory.from_dictionary(self, dictionary)
|
|
59
|
+
|
|
60
|
+
def combine_by_indexed_values(self: T, events: IntegerEvents, times: int = 1) -> T:
|
|
61
|
+
dictionary = DictCombiner(self.get_dict()).combine_by_indexed_values(events.get_dict(), times)
|
|
62
|
+
return EventsFactory.from_dictionary(self, dictionary)
|
|
63
|
+
|
|
64
|
+
def remove(self: T, events: IntegerEvents, times: int = 1) -> T:
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
:WARNING - UNSAFE METHOD: There is no record of what you added to an AdditiveEvents.
|
|
68
|
+
If you remove what you haven't added, no error will be raised, but you will have bugs.
|
|
69
|
+
"""
|
|
70
|
+
dictionary = DictCombiner(self.get_dict()).remove_by_tuple_list(events.get_dict(), times)
|
|
71
|
+
return EventsFactory.from_dictionary(self, dictionary)
|
dicetables/dicepool.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from typing import Dict, Tuple
|
|
2
|
+
|
|
3
|
+
from dicetables.eventsbases.protodie import ProtoDie
|
|
4
|
+
from dicetables.tools.orderedcombinations import ordered_combinations_of_events
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DicePool(object):
|
|
8
|
+
def __init__(self, input_die: ProtoDie, pool_size: int):
|
|
9
|
+
if pool_size < 1:
|
|
10
|
+
raise ValueError("Minimum DicePool size is 1")
|
|
11
|
+
self._input_die = input_die
|
|
12
|
+
self._pool_size = pool_size
|
|
13
|
+
self._rolls = ordered_combinations_of_events(input_die, pool_size)
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def die(self) -> ProtoDie:
|
|
17
|
+
return self._input_die
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def size(self) -> int:
|
|
21
|
+
return self._pool_size
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def rolls(self) -> Dict[Tuple[int, ...], int]:
|
|
25
|
+
return self._rolls.copy()
|
|
26
|
+
|
|
27
|
+
def __eq__(self, other):
|
|
28
|
+
if not isinstance(other, DicePool):
|
|
29
|
+
return False
|
|
30
|
+
return (self.die, self.size) == (other.die, other.size)
|
|
31
|
+
|
|
32
|
+
def __hash__(self):
|
|
33
|
+
return hash((self.die, self.size))
|
|
34
|
+
|
|
35
|
+
def __repr__(self):
|
|
36
|
+
return "{}({!r}, {})".format(self.__class__.__name__, self.die, self.size)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from dicetables.dieevents import ProtoDie
|
|
4
|
+
from dicetables.dicepool import DicePool
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class DicePoolCollection(ProtoDie):
|
|
8
|
+
"""
|
|
9
|
+
The abstract class for all DicePoolCollection objects. A DicePoolCollection creates a new die by selecting from a
|
|
10
|
+
DicePool. Select determines how many rolls are selected from the pool of total rolls.
|
|
11
|
+
Different implementations determine which particular rolls to select.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, pool: DicePool, select: int):
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
:param pool: the dice pool to select from
|
|
18
|
+
:param select: int <= `pool.size`. How many dice to select from the pool.
|
|
19
|
+
"""
|
|
20
|
+
self._dice_pool = pool
|
|
21
|
+
if select > self._dice_pool.size:
|
|
22
|
+
raise ValueError('you cannot select more dice than the pool_size.')
|
|
23
|
+
self._select = select
|
|
24
|
+
self._dict = self._generate_dict()
|
|
25
|
+
super(DicePoolCollection, self).__init__()
|
|
26
|
+
|
|
27
|
+
def _generate_dict(self):
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
def get_pool(self) -> DicePool:
|
|
31
|
+
return self._dice_pool
|
|
32
|
+
|
|
33
|
+
def get_select(self) -> int:
|
|
34
|
+
return self._select
|
|
35
|
+
|
|
36
|
+
def get_dict(self) -> Dict[int, int]:
|
|
37
|
+
return self._dict.copy()
|
|
38
|
+
|
|
39
|
+
def get_size(self) -> int:
|
|
40
|
+
return self._dice_pool.die.get_size() * self._select
|
|
41
|
+
|
|
42
|
+
def get_weight(self) -> int:
|
|
43
|
+
return self._dice_pool.die.get_weight() * self._select
|
|
44
|
+
|
|
45
|
+
def weight_info(self):
|
|
46
|
+
input_die = self._dice_pool.die
|
|
47
|
+
original_weight_info = input_die.weight_info().replace(str(input_die), 'input_die info:')
|
|
48
|
+
return '{}\n{}'.format(self, original_weight_info)
|
|
49
|
+
|
|
50
|
+
def multiply_str(self, number):
|
|
51
|
+
return '{}({})'.format(number, self)
|
|
52
|
+
|
|
53
|
+
def __str__(self):
|
|
54
|
+
descriptor = self.__class__.__name__.replace('OfDicePool', '')
|
|
55
|
+
return '{} {} of {}'.format(descriptor, self._select, self._dice_pool.die.multiply_str(self._dice_pool.size))
|
|
56
|
+
|
|
57
|
+
def __repr__(self):
|
|
58
|
+
return '{}({!r}, {})'.format(
|
|
59
|
+
self.__class__.__name__, self._dice_pool, self._select
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class BestOfDicePool(DicePoolCollection):
|
|
64
|
+
"""
|
|
65
|
+
Take the best [select] rolls from a DicePool of [pool_size] * [input_die].
|
|
66
|
+
BestOfDicePool(DicePool(Die(6), 4), 3) is the best 3 rolls from four six-sided dice.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(self, pool: DicePool, select: int):
|
|
70
|
+
super().__init__(pool, select)
|
|
71
|
+
|
|
72
|
+
def _generate_dict(self):
|
|
73
|
+
ordered_combinations = self._dice_pool.rolls
|
|
74
|
+
start_at = self._dice_pool.size - self._select
|
|
75
|
+
stop_before = self._dice_pool.size
|
|
76
|
+
return generate_events_dict(ordered_combinations, start_at, stop_before)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class WorstOfDicePool(DicePoolCollection):
|
|
80
|
+
"""
|
|
81
|
+
Take the worst [select] rolls from a DicePool of [pool_size] * [input_die].
|
|
82
|
+
WorstOfDicePool(DicePool(Die(6), 4), 3) is the worst 3 rolls from four six-sided dice.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self, pool: DicePool, select: int):
|
|
86
|
+
super().__init__(pool, select)
|
|
87
|
+
|
|
88
|
+
def _generate_dict(self):
|
|
89
|
+
ordered_combinations = self._dice_pool.rolls
|
|
90
|
+
start_at = 0
|
|
91
|
+
stop_before = self._select
|
|
92
|
+
return generate_events_dict(ordered_combinations, start_at, stop_before)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class UpperMidOfDicePool(DicePoolCollection):
|
|
96
|
+
"""
|
|
97
|
+
Take the middle [select] rolls from a DicePool of [pool_size] * [input_die].
|
|
98
|
+
UpperMidOfDicePool(DicePool(Die(6), 5), 3) is the middle 3 rolls from five six-sided dice.
|
|
99
|
+
|
|
100
|
+
If there is no perfect middle, take the higher of two choices. For five dice that roll
|
|
101
|
+
(1, 1, 2, 3, 4), select=3 takes (1, 2, 3) and select=2 takes (2, 3).
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def __init__(self, pool: DicePool, select: int):
|
|
105
|
+
super().__init__(pool, select)
|
|
106
|
+
|
|
107
|
+
def _generate_dict(self):
|
|
108
|
+
ordered_combinations = self._dice_pool.rolls
|
|
109
|
+
end_slice, extra = divmod(self._dice_pool.size - self._select, 2)
|
|
110
|
+
start_at = end_slice + extra
|
|
111
|
+
stop_before = self._dice_pool.size - end_slice
|
|
112
|
+
return generate_events_dict(ordered_combinations, start_at, stop_before)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class LowerMidOfDicePool(DicePoolCollection):
|
|
116
|
+
"""
|
|
117
|
+
Take the middle [select] rolls from a DicePool of [pool_size] * [input_die].
|
|
118
|
+
LowerMidOfDicePool(DicePool(Die(6), 5), 3) is the middle 3 rolls from five six-sided dice.
|
|
119
|
+
|
|
120
|
+
If there is no perfect middle, take the lower of two choices. For five dice that roll
|
|
121
|
+
(1, 1, 2, 3, 4), select=3 takes (1, 2, 3) and select=2 takes (1, 2).
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
def __init__(self, pool: DicePool, select: int):
|
|
125
|
+
super().__init__(pool, select)
|
|
126
|
+
|
|
127
|
+
def _generate_dict(self):
|
|
128
|
+
ordered_combinations = self._dice_pool.rolls
|
|
129
|
+
end_slice, extra = divmod(self._dice_pool.size - self._select, 2)
|
|
130
|
+
start_at = end_slice
|
|
131
|
+
stop_before = self._dice_pool.size - (end_slice + extra)
|
|
132
|
+
return generate_events_dict(ordered_combinations, start_at, stop_before)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def generate_events_dict(ordered_combinations, start_at, stop_before) -> Dict[int, int]:
|
|
136
|
+
master_dict = {}
|
|
137
|
+
for key, val in ordered_combinations.items():
|
|
138
|
+
master_key = sum(key[start_at:stop_before])
|
|
139
|
+
master_dict[master_key] = master_dict.get(master_key, 0) + val
|
|
140
|
+
return master_dict
|
dicetables/dicerecord.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
An immutable record of dice added to and removed from DiceTable
|
|
3
|
+
"""
|
|
4
|
+
from typing import Dict
|
|
5
|
+
|
|
6
|
+
from dicetables.eventsbases.eventerrors import DiceRecordError
|
|
7
|
+
from dicetables.eventsbases.protodie import ProtoDie
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RecordVerifier(object):
|
|
11
|
+
@staticmethod
|
|
12
|
+
def check_types(die_input):
|
|
13
|
+
if any(not isinstance(die, ProtoDie) or not isinstance(num, int) for die, num in die_input.items()):
|
|
14
|
+
raise DiceRecordError('input must be {ProtoDie: int, ...}')
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def check_negative(die_input):
|
|
18
|
+
for key, val in die_input.items():
|
|
19
|
+
if val < 0:
|
|
20
|
+
raise DiceRecordError('Tried to create a DiceRecord with a negative value at {!r}: {}'.format(key, val))
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def check_number(num):
|
|
24
|
+
if num < 0:
|
|
25
|
+
raise DiceRecordError('Tried to add_die or remove_die with a negative number.')
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def scrub_zeroes(input_dict):
|
|
29
|
+
return {key: val for key, val in input_dict.items() if val}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class DiceRecord(object):
|
|
33
|
+
def __init__(self, dice_number_dict: Dict[ProtoDie, int]):
|
|
34
|
+
RecordVerifier.check_types(dice_number_dict)
|
|
35
|
+
RecordVerifier.check_negative(dice_number_dict)
|
|
36
|
+
self._record = scrub_zeroes(dice_number_dict)
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def new(cls) -> 'DiceRecord':
|
|
40
|
+
return cls({})
|
|
41
|
+
|
|
42
|
+
def get_dict(self) -> Dict[ProtoDie, int]:
|
|
43
|
+
return self._record.copy()
|
|
44
|
+
|
|
45
|
+
def get_number(self, query_die: ProtoDie) -> int:
|
|
46
|
+
return self._record.get(query_die, 0)
|
|
47
|
+
|
|
48
|
+
def add_die(self, die: ProtoDie, times: int) -> 'DiceRecord':
|
|
49
|
+
RecordVerifier.check_number(times)
|
|
50
|
+
new = self._record.copy()
|
|
51
|
+
new[die] = times + self.get_number(die)
|
|
52
|
+
return DiceRecord(new)
|
|
53
|
+
|
|
54
|
+
def remove_die(self, die: ProtoDie, times: int) -> 'DiceRecord':
|
|
55
|
+
RecordVerifier.check_number(times)
|
|
56
|
+
new = self._record.copy()
|
|
57
|
+
new[die] = self.get_number(die) - times
|
|
58
|
+
return DiceRecord(new)
|
|
59
|
+
|
|
60
|
+
def __eq__(self, other):
|
|
61
|
+
if not isinstance(other, DiceRecord):
|
|
62
|
+
return False
|
|
63
|
+
return self.get_dict() == other.get_dict()
|
|
64
|
+
|
|
65
|
+
def __ne__(self, other):
|
|
66
|
+
return not self == other
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return 'DiceRecord({!r})'.format(self._record)
|
dicetables/dicetable.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DiceTable and DetailedDiceTable compute combination of Die events
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import TypeVar
|
|
6
|
+
|
|
7
|
+
from dicetables import DiceRecord
|
|
8
|
+
from dicetables.additiveevents import AdditiveEvents
|
|
9
|
+
from dicetables.eventsbases.protodie import ProtoDie
|
|
10
|
+
from dicetables.eventsinfo import EventsCalculations, EventsInformation
|
|
11
|
+
from dicetables.factory.eventsfactory import EventsFactory
|
|
12
|
+
from dicetables.tools.dictcombiner import DictCombiner
|
|
13
|
+
|
|
14
|
+
T = TypeVar('T', bound='DiceTable')
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DiceTable(AdditiveEvents):
|
|
18
|
+
def __init__(self, events_dict: dict, dice_record: DiceRecord):
|
|
19
|
+
self._record = dice_record
|
|
20
|
+
super(DiceTable, self).__init__(events_dict)
|
|
21
|
+
|
|
22
|
+
def dice_data(self) -> DiceRecord:
|
|
23
|
+
return self._record
|
|
24
|
+
|
|
25
|
+
def get_list(self):
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
:return: sorted copy of dice list: [(die, number of dice), ...]
|
|
29
|
+
"""
|
|
30
|
+
return sorted(self._record.get_dict().items())
|
|
31
|
+
|
|
32
|
+
def number_of_dice(self, query_die: ProtoDie) -> int:
|
|
33
|
+
return self._record.get_number(query_die)
|
|
34
|
+
|
|
35
|
+
def weights_info(self):
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
:return: str: complete info for all dice
|
|
39
|
+
"""
|
|
40
|
+
output = ''
|
|
41
|
+
for die, number in self.get_list():
|
|
42
|
+
output += format_die_info(die, number)
|
|
43
|
+
return output.rstrip('\n')
|
|
44
|
+
|
|
45
|
+
def __str__(self):
|
|
46
|
+
str_list = [die.multiply_str(number) for die, number in self.get_list()]
|
|
47
|
+
return '\n'.join(str_list)
|
|
48
|
+
|
|
49
|
+
def __repr__(self):
|
|
50
|
+
class_name = self.__class__.__name__
|
|
51
|
+
dice = str(self).replace('\n', ', ')
|
|
52
|
+
return '<{} containing [{}]>'.format(class_name, dice)
|
|
53
|
+
|
|
54
|
+
def add_die(self: T, die: ProtoDie, times: int = 1) -> T:
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
:param die: any subclass of ProtoDie: Die, ModDie, WeightedDie, ModWeightedDie, Modifier,
|
|
58
|
+
StrongDie, Exploding, ExplodingOn
|
|
59
|
+
:param times: int>= 0
|
|
60
|
+
"""
|
|
61
|
+
dice_data = self._record.add_die(die, times)
|
|
62
|
+
dictionary = DictCombiner(self.get_dict()).combine_by_fastest(die.get_dict(), times)
|
|
63
|
+
return EventsFactory.from_dictionary_and_dice(self, dictionary, dice_data)
|
|
64
|
+
|
|
65
|
+
def remove_die(self: T, die: ProtoDie, times=1) -> T:
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
:param die: any subclass of ProtoDie: Die, ModDie, WeightedDie, ModWeightedDie, Modifier,
|
|
69
|
+
StrongDie, Exploding, ExplodingOn
|
|
70
|
+
:param times: 0 <= int <= number of "die" in table
|
|
71
|
+
"""
|
|
72
|
+
dice_data = self._record.remove_die(die, times)
|
|
73
|
+
dictionary = DictCombiner(self.get_dict()).remove_by_tuple_list(die.get_dict(), times)
|
|
74
|
+
return EventsFactory.from_dictionary_and_dice(self, dictionary, dice_data)
|
|
75
|
+
|
|
76
|
+
def __eq__(self, other):
|
|
77
|
+
return super(DiceTable, self).__eq__(other) and self.dice_data() == other.dice_data()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def format_die_info(die, number):
|
|
81
|
+
weight_info = die.weight_info()
|
|
82
|
+
adjusted_info = weight_info.replace(str(die), die.multiply_str(number)) + '\n\n'
|
|
83
|
+
return adjusted_info
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class DetailedDiceTable(DiceTable):
|
|
87
|
+
def __init__(self, events_dict: dict, dice_record: DiceRecord, calc_includes_zeroes: bool = True):
|
|
88
|
+
super(DetailedDiceTable, self).__init__(events_dict, dice_record)
|
|
89
|
+
self._calc = EventsCalculations(self, calc_includes_zeroes)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def info(self) -> EventsInformation:
|
|
93
|
+
return self._calc.info
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def calc(self) -> EventsCalculations:
|
|
97
|
+
return self._calc
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def calc_includes_zeroes(self) -> bool:
|
|
101
|
+
return self._calc.include_zeroes
|
|
102
|
+
|
|
103
|
+
def switch_boolean(self: T) -> T:
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
:return: a new DetailedDiceTable with the `calc_includes_zeroes` boolean switched
|
|
107
|
+
"""
|
|
108
|
+
return EventsFactory.from_params(self, {'calc_includes_zeroes': not self.calc_includes_zeroes})
|
|
109
|
+
|
|
110
|
+
def __eq__(self, other):
|
|
111
|
+
return super(DetailedDiceTable, self).__eq__(other) and self.calc_includes_zeroes == other.calc_includes_zeroes
|