1import copy
2
3
4class EffectWrapper:
5 """Effect decorator to handle intensity and single-usage."""
6
7 def __init__(self, function, intensity):
8 """Initializator."""
9 self.function = function
10 self.intensity = intensity
11 self.depleted = False
12
13 def __call__(self, target):
14 """Wrapper of the decorated function."""
15 if self.depleted:
16 raise TypeError("Effect is depleted.")
17 self.depleted = True
18 for _ in range(self.intensity):
19 self.function(target)
20
21
22class Potion:
23 """Potion representation."""
24
25 def __init__(self, effects, duration, intensities=None):
26 """Initializator."""
27 self.effects = effects
28 self.duration = duration
29 self.deprecated = False
30 self.depleted = False
31 self.intensities = intensities or {}
32 for key, val in effects.items():
33 self.intensities.setdefault(key, 1)
34 setattr(self, key, EffectWrapper(val, self.intensities[key]))
35
36 def __getattribute__(self, name):
37 """Prevent usage if the potion is deprecated."""
38 try:
39 if object.__getattribute__(self, 'depleted'):
40 raise TypeError("Potion is depleted.")
41 if object.__getattribute__(self, 'deprecated'):
42 raise TypeError("Potion is now part of something bigger than itself.")
43 if type(object.__getattribute__(self, name)) is EffectWrapper:
44 object.__getattribute__(self, 'intensities')[name] = 0
45 except AttributeError:
46 pass # deepcopy starts using this code before depleted and deprecated are available
47 return object.__getattribute__(self, name)
48
49 @staticmethod
50 def round_down(val):
51 """Round x.5 down, other cases - as normal."""
52 if val % 1 <= 0.5:
53 return int(val)
54 return int(val) + 1
55
56 def __add__(self, other):
57 """Combinations."""
58 effects = {}
59 intensities = {}
60 duration = max(self.duration, other.duration)
61 for key, val in self.effects.items():
62 intensities[key] = self.intensities[key]
63 effects[key] = val
64 for key, val in other.effects.items():
65 intensities[key] = intensities.get(key, 0) + other.intensities[key]
66 effects[key] = val
67 self.deprecated = True
68 other.deprecated = True
69 return Potion(effects, duration, intensities)
70
71 def __mul__(self, other):
72 """Potentiation/Dilution."""
73 effects = {}
74 intensities = {}
75 duration = self.duration
76 for key, val in self.effects.items():
77 intensities[key] = self.round_down(self.intensities[key] * other)
78 effects[key] = val
79 self.deprecated = True
80 return Potion(effects, duration, intensities)
81
82 def __sub__(self, other):
83 """Purification."""
84 effects = {}
85 intensities = {}
86 duration = self.duration
87 if set(other.effects) - set(self.effects):
88 raise TypeError('Effect mismatch.')
89 for key, val in self.effects.items():
90 if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0:
91 intensities[key] = intensity
92 effects[key] = val
93 self.deprecated = True
94 other.deprecated = True
95 return Potion(effects, duration, intensities)
96
97 def __truediv__(self, other):
98 """Separation."""
99 effects = {}
100 intensities = {}
101 duration = self.duration
102 for key, val in self.effects.items():
103 intensities[key] = self.round_down(self.intensities[key] / other)
104 effects[key] = val
105 self.deprecated = True
106 return tuple([Potion(effects, duration, intensities) for _ in range(other)])
107
108 def __eq__(self, other):
109 """Equal."""
110 return self.intensities == other.intensities
111
112 def __lt__(self, other):
113 """Less than."""
114 return sum(self.intensities.values()) < sum(other.intensities.values())
115
116
117class ГоспожатаПоХимия:
118 """Химичка Димитричка."""
119
120 def __init__(self):
121 """Initializator."""
122 self._target_cache = {}
123 self._potions_stack = {}
124
125 def _cache_target(self, target):
126 """Cache all public properties of a target."""
127 if target in self._target_cache:
128 return False
129 self._target_cache[target] = {}
130 for key, val in vars(target).items():
131 if key.startswith('_'):
132 continue
133 self._target_cache[target][key] = copy.deepcopy(val)
134
135 def _apply_potion(self, target, potion):
136 """Apply a potion to a target."""
137 potion_copy = copy.deepcopy(potion)
138 sorted_effects = sorted(potion.effects.keys(),
139 key=lambda x: sum(map(ord, x)),
140 reverse=True)
141 for key in sorted_effects:
142 try:
143 getattr(potion, key)(target)
144 except TypeError:
145 pass # This effect is not available
146 potion.depleted = True
147 return potion_copy
148
149 def _apply_all_potions(self, target):
150 """Apply all potions to a target one by one."""
151 # Set initial state
152 for attr_key, attr_val in self._target_cache[target].items():
153 setattr(target, attr_key, copy.deepcopy(attr_val))
154 # Apply all active potions
155 # and replace the used potion with a potion ready to be used again
156 if target in self._potions_stack:
157 for i, potion in enumerate(self._potions_stack[target]):
158 potion_copy = self._apply_potion(target, potion)
159 self._potions_stack[target][i] = potion_copy
160
161 def apply(self, target, potion):
162 """Apply a potion to a target."""
163 # Ensure potion is not depleted by trying to get an attribute
164 # Depleted potion will raise a TypeError if it is depleted
165 potion.depleted
166 self._cache_target(target)
167 self._potions_stack.setdefault(target, []).append(potion)
168 self._apply_all_potions(target)
169
170 def tick(self):
171 """Tick a turn."""
172 for target, potion_stack in self._potions_stack.items():
173 new_potion_stack = []
174 for potion in potion_stack:
175 potion.duration -= 1
176 if potion.duration > 0:
177 new_potion_stack.append(potion)
178 self._potions_stack[target] = new_potion_stack
179 self._apply_all_potions(target)
180 if not len(new_potion_stack):
181 del self._target_cache[target]
182 self._potions_stack = {key: val for key, val in self._potions_stack.items() if val}
183
184
185
186class Target:
187 """Test target."""
188
189 def __init__(self, **kwargs):
190 """Initializator."""
191 self._cache = kwargs
192 for key, val in kwargs.items():
193 setattr(self, key, val)
....................
----------------------------------------------------------------------
Ran 20 tests in 0.003s
OK
| f | 1 | import copy | f | 1 | import copy |
| 2 | 2 | ||||
| 3 | 3 | ||||
| 4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
| 5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
| 6 | 6 | ||||
| 7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
| 8 | """Initializator.""" | 8 | """Initializator.""" | ||
| 9 | self.function = function | 9 | self.function = function | ||
| 10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
| 11 | self.depleted = False | 11 | self.depleted = False | ||
| 12 | 12 | ||||
| 13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
| 14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
| 15 | if self.depleted: | 15 | if self.depleted: | ||
| 16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
| 17 | self.depleted = True | 17 | self.depleted = True | ||
| 18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
| 19 | self.function(target) | 19 | self.function(target) | ||
| 20 | 20 | ||||
| 21 | 21 | ||||
| 22 | class Potion: | 22 | class Potion: | ||
| 23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
| 24 | 24 | ||||
| 25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
| 26 | """Initializator.""" | 26 | """Initializator.""" | ||
| 27 | self.effects = effects | 27 | self.effects = effects | ||
| 28 | self.duration = duration | 28 | self.duration = duration | ||
| 29 | self.deprecated = False | 29 | self.deprecated = False | ||
| 30 | self.depleted = False | 30 | self.depleted = False | ||
| 31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
| 32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
| 33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
| 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
| 35 | 35 | ||||
| 36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
| 37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
| 38 | try: | 38 | try: | ||
| 39 | if object.__getattribute__(self, 'depleted'): | 39 | if object.__getattribute__(self, 'depleted'): | ||
| 40 | raise TypeError("Potion is depleted.") | 40 | raise TypeError("Potion is depleted.") | ||
| 41 | if object.__getattribute__(self, 'deprecated'): | 41 | if object.__getattribute__(self, 'deprecated'): | ||
| 42 | raise TypeError("Potion is now part of something bigger than itself.") | 42 | raise TypeError("Potion is now part of something bigger than itself.") | ||
| n | n | 43 | if type(object.__getattribute__(self, name)) is EffectWrapper: | ||
| 44 | object.__getattribute__(self, 'intensities')[name] = 0 | ||||
| 43 | except AttributeError: | 45 | except AttributeError: | ||
| 44 | pass # deepcopy starts using this code before depleted and deprecated are available | 46 | pass # deepcopy starts using this code before depleted and deprecated are available | ||
| 45 | return object.__getattribute__(self, name) | 47 | return object.__getattribute__(self, name) | ||
| 46 | 48 | ||||
| 47 | @staticmethod | 49 | @staticmethod | ||
| 48 | def round_down(val): | 50 | def round_down(val): | ||
| 49 | """Round x.5 down, other cases - as normal.""" | 51 | """Round x.5 down, other cases - as normal.""" | ||
| 50 | if val % 1 <= 0.5: | 52 | if val % 1 <= 0.5: | ||
| 51 | return int(val) | 53 | return int(val) | ||
| 52 | return int(val) + 1 | 54 | return int(val) + 1 | ||
| 53 | 55 | ||||
| 54 | def __add__(self, other): | 56 | def __add__(self, other): | ||
| 55 | """Combinations.""" | 57 | """Combinations.""" | ||
| 56 | effects = {} | 58 | effects = {} | ||
| 57 | intensities = {} | 59 | intensities = {} | ||
| 58 | duration = max(self.duration, other.duration) | 60 | duration = max(self.duration, other.duration) | ||
| 59 | for key, val in self.effects.items(): | 61 | for key, val in self.effects.items(): | ||
| 60 | intensities[key] = self.intensities[key] | 62 | intensities[key] = self.intensities[key] | ||
| 61 | effects[key] = val | 63 | effects[key] = val | ||
| 62 | for key, val in other.effects.items(): | 64 | for key, val in other.effects.items(): | ||
| 63 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 65 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
| 64 | effects[key] = val | 66 | effects[key] = val | ||
| 65 | self.deprecated = True | 67 | self.deprecated = True | ||
| 66 | other.deprecated = True | 68 | other.deprecated = True | ||
| 67 | return Potion(effects, duration, intensities) | 69 | return Potion(effects, duration, intensities) | ||
| 68 | 70 | ||||
| 69 | def __mul__(self, other): | 71 | def __mul__(self, other): | ||
| 70 | """Potentiation/Dilution.""" | 72 | """Potentiation/Dilution.""" | ||
| 71 | effects = {} | 73 | effects = {} | ||
| 72 | intensities = {} | 74 | intensities = {} | ||
| 73 | duration = self.duration | 75 | duration = self.duration | ||
| 74 | for key, val in self.effects.items(): | 76 | for key, val in self.effects.items(): | ||
| 75 | intensities[key] = self.round_down(self.intensities[key] * other) | 77 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
| 76 | effects[key] = val | 78 | effects[key] = val | ||
| 77 | self.deprecated = True | 79 | self.deprecated = True | ||
| 78 | return Potion(effects, duration, intensities) | 80 | return Potion(effects, duration, intensities) | ||
| 79 | 81 | ||||
| 80 | def __sub__(self, other): | 82 | def __sub__(self, other): | ||
| 81 | """Purification.""" | 83 | """Purification.""" | ||
| 82 | effects = {} | 84 | effects = {} | ||
| 83 | intensities = {} | 85 | intensities = {} | ||
| 84 | duration = self.duration | 86 | duration = self.duration | ||
| 85 | if set(other.effects) - set(self.effects): | 87 | if set(other.effects) - set(self.effects): | ||
| 86 | raise TypeError('Effect mismatch.') | 88 | raise TypeError('Effect mismatch.') | ||
| 87 | for key, val in self.effects.items(): | 89 | for key, val in self.effects.items(): | ||
| 88 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | 90 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | ||
| 89 | intensities[key] = intensity | 91 | intensities[key] = intensity | ||
| 90 | effects[key] = val | 92 | effects[key] = val | ||
| 91 | self.deprecated = True | 93 | self.deprecated = True | ||
| 92 | other.deprecated = True | 94 | other.deprecated = True | ||
| 93 | return Potion(effects, duration, intensities) | 95 | return Potion(effects, duration, intensities) | ||
| 94 | 96 | ||||
| 95 | def __truediv__(self, other): | 97 | def __truediv__(self, other): | ||
| 96 | """Separation.""" | 98 | """Separation.""" | ||
| 97 | effects = {} | 99 | effects = {} | ||
| 98 | intensities = {} | 100 | intensities = {} | ||
| 99 | duration = self.duration | 101 | duration = self.duration | ||
| 100 | for key, val in self.effects.items(): | 102 | for key, val in self.effects.items(): | ||
| 101 | intensities[key] = self.round_down(self.intensities[key] / other) | 103 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
| 102 | effects[key] = val | 104 | effects[key] = val | ||
| 103 | self.deprecated = True | 105 | self.deprecated = True | ||
| 104 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | 106 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | ||
| 105 | 107 | ||||
| 106 | def __eq__(self, other): | 108 | def __eq__(self, other): | ||
| 107 | """Equal.""" | 109 | """Equal.""" | ||
| 108 | return self.intensities == other.intensities | 110 | return self.intensities == other.intensities | ||
| 109 | 111 | ||||
| 110 | def __lt__(self, other): | 112 | def __lt__(self, other): | ||
| 111 | """Less than.""" | 113 | """Less than.""" | ||
| 112 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 114 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
| 113 | 115 | ||||
| 114 | 116 | ||||
| 115 | class ГоспожатаПоХимия: | 117 | class ГоспожатаПоХимия: | ||
| 116 | """Химичка Димитричка.""" | 118 | """Химичка Димитричка.""" | ||
| 117 | 119 | ||||
| 118 | def __init__(self): | 120 | def __init__(self): | ||
| 119 | """Initializator.""" | 121 | """Initializator.""" | ||
| 120 | self._target_cache = {} | 122 | self._target_cache = {} | ||
| 121 | self._potions_stack = {} | 123 | self._potions_stack = {} | ||
| 122 | 124 | ||||
| 123 | def _cache_target(self, target): | 125 | def _cache_target(self, target): | ||
| 124 | """Cache all public properties of a target.""" | 126 | """Cache all public properties of a target.""" | ||
| 125 | if target in self._target_cache: | 127 | if target in self._target_cache: | ||
| 126 | return False | 128 | return False | ||
| 127 | self._target_cache[target] = {} | 129 | self._target_cache[target] = {} | ||
| 128 | for key, val in vars(target).items(): | 130 | for key, val in vars(target).items(): | ||
| 129 | if key.startswith('_'): | 131 | if key.startswith('_'): | ||
| 130 | continue | 132 | continue | ||
| 131 | self._target_cache[target][key] = copy.deepcopy(val) | 133 | self._target_cache[target][key] = copy.deepcopy(val) | ||
| 132 | 134 | ||||
| 133 | def _apply_potion(self, target, potion): | 135 | def _apply_potion(self, target, potion): | ||
| 134 | """Apply a potion to a target.""" | 136 | """Apply a potion to a target.""" | ||
| 135 | potion_copy = copy.deepcopy(potion) | 137 | potion_copy = copy.deepcopy(potion) | ||
| 136 | sorted_effects = sorted(potion.effects.keys(), | 138 | sorted_effects = sorted(potion.effects.keys(), | ||
| 137 | key=lambda x: sum(map(ord, x)), | 139 | key=lambda x: sum(map(ord, x)), | ||
| 138 | reverse=True) | 140 | reverse=True) | ||
| 139 | for key in sorted_effects: | 141 | for key in sorted_effects: | ||
| 140 | try: | 142 | try: | ||
| 141 | getattr(potion, key)(target) | 143 | getattr(potion, key)(target) | ||
| 142 | except TypeError: | 144 | except TypeError: | ||
| 143 | pass # This effect is not available | 145 | pass # This effect is not available | ||
| 144 | potion.depleted = True | 146 | potion.depleted = True | ||
| 145 | return potion_copy | 147 | return potion_copy | ||
| 146 | 148 | ||||
| 147 | def _apply_all_potions(self, target): | 149 | def _apply_all_potions(self, target): | ||
| 148 | """Apply all potions to a target one by one.""" | 150 | """Apply all potions to a target one by one.""" | ||
| 149 | # Set initial state | 151 | # Set initial state | ||
| 150 | for attr_key, attr_val in self._target_cache[target].items(): | 152 | for attr_key, attr_val in self._target_cache[target].items(): | ||
| 151 | setattr(target, attr_key, copy.deepcopy(attr_val)) | 153 | setattr(target, attr_key, copy.deepcopy(attr_val)) | ||
| 152 | # Apply all active potions | 154 | # Apply all active potions | ||
| 153 | # and replace the used potion with a potion ready to be used again | 155 | # and replace the used potion with a potion ready to be used again | ||
| 154 | if target in self._potions_stack: | 156 | if target in self._potions_stack: | ||
| 155 | for i, potion in enumerate(self._potions_stack[target]): | 157 | for i, potion in enumerate(self._potions_stack[target]): | ||
| 156 | potion_copy = self._apply_potion(target, potion) | 158 | potion_copy = self._apply_potion(target, potion) | ||
| 157 | self._potions_stack[target][i] = potion_copy | 159 | self._potions_stack[target][i] = potion_copy | ||
| 158 | 160 | ||||
| 159 | def apply(self, target, potion): | 161 | def apply(self, target, potion): | ||
| 160 | """Apply a potion to a target.""" | 162 | """Apply a potion to a target.""" | ||
| 161 | # Ensure potion is not depleted by trying to get an attribute | 163 | # Ensure potion is not depleted by trying to get an attribute | ||
| 162 | # Depleted potion will raise a TypeError if it is depleted | 164 | # Depleted potion will raise a TypeError if it is depleted | ||
| 163 | potion.depleted | 165 | potion.depleted | ||
| 164 | self._cache_target(target) | 166 | self._cache_target(target) | ||
| 165 | self._potions_stack.setdefault(target, []).append(potion) | 167 | self._potions_stack.setdefault(target, []).append(potion) | ||
| 166 | self._apply_all_potions(target) | 168 | self._apply_all_potions(target) | ||
| 167 | 169 | ||||
| 168 | def tick(self): | 170 | def tick(self): | ||
| 169 | """Tick a turn.""" | 171 | """Tick a turn.""" | ||
| 170 | for target, potion_stack in self._potions_stack.items(): | 172 | for target, potion_stack in self._potions_stack.items(): | ||
| 171 | new_potion_stack = [] | 173 | new_potion_stack = [] | ||
| 172 | for potion in potion_stack: | 174 | for potion in potion_stack: | ||
| 173 | potion.duration -= 1 | 175 | potion.duration -= 1 | ||
| 174 | if potion.duration > 0: | 176 | if potion.duration > 0: | ||
| 175 | new_potion_stack.append(potion) | 177 | new_potion_stack.append(potion) | ||
| 176 | self._potions_stack[target] = new_potion_stack | 178 | self._potions_stack[target] = new_potion_stack | ||
| 177 | self._apply_all_potions(target) | 179 | self._apply_all_potions(target) | ||
| 178 | if not len(new_potion_stack): | 180 | if not len(new_potion_stack): | ||
| 179 | del self._target_cache[target] | 181 | del self._target_cache[target] | ||
| 180 | self._potions_stack = {key: val for key, val in self._potions_stack.items() if val} | 182 | self._potions_stack = {key: val for key, val in self._potions_stack.items() if val} | ||
| 181 | 183 | ||||
| t | t | 184 | |||
| 185 | |||||
| 186 | class Target: | ||||
| 187 | """Test target.""" | ||||
| 188 | |||||
| 189 | def __init__(self, **kwargs): | ||||
| 190 | """Initializator.""" | ||||
| 191 | self._cache = kwargs | ||||
| 192 | for key, val in kwargs.items(): | ||||
| 193 | setattr(self, key, val) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import copy | f | 1 | import copy |
| 2 | 2 | ||||
| 3 | 3 | ||||
| 4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
| 5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
| 6 | 6 | ||||
| 7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
| 8 | """Initializator.""" | 8 | """Initializator.""" | ||
| 9 | self.function = function | 9 | self.function = function | ||
| 10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
| 11 | self.depleted = False | 11 | self.depleted = False | ||
| 12 | 12 | ||||
| 13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
| 14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
| 15 | if self.depleted: | 15 | if self.depleted: | ||
| 16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
| 17 | self.depleted = True | 17 | self.depleted = True | ||
| 18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
| 19 | self.function(target) | 19 | self.function(target) | ||
| 20 | 20 | ||||
| 21 | 21 | ||||
| 22 | class Potion: | 22 | class Potion: | ||
| 23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
| 24 | 24 | ||||
| 25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
| 26 | """Initializator.""" | 26 | """Initializator.""" | ||
| 27 | self.effects = effects | 27 | self.effects = effects | ||
| 28 | self.duration = duration | 28 | self.duration = duration | ||
| 29 | self.deprecated = False | 29 | self.deprecated = False | ||
| 30 | self.depleted = False | 30 | self.depleted = False | ||
| 31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
| 32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
| 33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
| 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
| 35 | 35 | ||||
| 36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
| 37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
| n | n | 38 | try: | ||
| 38 | if object.__getattribute__(self, 'depleted'): | 39 | if object.__getattribute__(self, 'depleted'): | ||
| 39 | raise TypeError("Potion is depleted.") | 40 | raise TypeError("Potion is depleted.") | ||
| 40 | if object.__getattribute__(self, 'deprecated'): | 41 | if object.__getattribute__(self, 'deprecated'): | ||
| 41 | raise TypeError("Potion is now part of something bigger than itself.") | 42 | raise TypeError("Potion is now part of something bigger than itself.") | ||
| 43 | except AttributeError: | ||||
| 44 | pass # deepcopy starts using this code before depleted and deprecated are available | ||||
| 42 | return object.__getattribute__(self, name) | 45 | return object.__getattribute__(self, name) | ||
| 43 | 46 | ||||
| 44 | @staticmethod | 47 | @staticmethod | ||
| 45 | def round_down(val): | 48 | def round_down(val): | ||
| 46 | """Round x.5 down, other cases - as normal.""" | 49 | """Round x.5 down, other cases - as normal.""" | ||
| 47 | if val % 1 <= 0.5: | 50 | if val % 1 <= 0.5: | ||
| 48 | return int(val) | 51 | return int(val) | ||
| 49 | return int(val) + 1 | 52 | return int(val) + 1 | ||
| 50 | 53 | ||||
| 51 | def __add__(self, other): | 54 | def __add__(self, other): | ||
| 52 | """Combinations.""" | 55 | """Combinations.""" | ||
| 53 | effects = {} | 56 | effects = {} | ||
| 54 | intensities = {} | 57 | intensities = {} | ||
| 55 | duration = max(self.duration, other.duration) | 58 | duration = max(self.duration, other.duration) | ||
| 56 | for key, val in self.effects.items(): | 59 | for key, val in self.effects.items(): | ||
| 57 | intensities[key] = self.intensities[key] | 60 | intensities[key] = self.intensities[key] | ||
| 58 | effects[key] = val | 61 | effects[key] = val | ||
| 59 | for key, val in other.effects.items(): | 62 | for key, val in other.effects.items(): | ||
| 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 63 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
| 61 | effects[key] = val | 64 | effects[key] = val | ||
| 62 | self.deprecated = True | 65 | self.deprecated = True | ||
| 63 | other.deprecated = True | 66 | other.deprecated = True | ||
| 64 | return Potion(effects, duration, intensities) | 67 | return Potion(effects, duration, intensities) | ||
| 65 | 68 | ||||
| 66 | def __mul__(self, other): | 69 | def __mul__(self, other): | ||
| 67 | """Potentiation/Dilution.""" | 70 | """Potentiation/Dilution.""" | ||
| 68 | effects = {} | 71 | effects = {} | ||
| 69 | intensities = {} | 72 | intensities = {} | ||
| 70 | duration = self.duration | 73 | duration = self.duration | ||
| 71 | for key, val in self.effects.items(): | 74 | for key, val in self.effects.items(): | ||
| 72 | intensities[key] = self.round_down(self.intensities[key] * other) | 75 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
| 73 | effects[key] = val | 76 | effects[key] = val | ||
| 74 | self.deprecated = True | 77 | self.deprecated = True | ||
| 75 | return Potion(effects, duration, intensities) | 78 | return Potion(effects, duration, intensities) | ||
| 76 | 79 | ||||
| 77 | def __sub__(self, other): | 80 | def __sub__(self, other): | ||
| 78 | """Purification.""" | 81 | """Purification.""" | ||
| 79 | effects = {} | 82 | effects = {} | ||
| 80 | intensities = {} | 83 | intensities = {} | ||
| 81 | duration = self.duration | 84 | duration = self.duration | ||
| 82 | if set(other.effects) - set(self.effects): | 85 | if set(other.effects) - set(self.effects): | ||
| 83 | raise TypeError('Effect mismatch.') | 86 | raise TypeError('Effect mismatch.') | ||
| 84 | for key, val in self.effects.items(): | 87 | for key, val in self.effects.items(): | ||
| 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | 88 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | ||
| 86 | intensities[key] = intensity | 89 | intensities[key] = intensity | ||
| 87 | effects[key] = val | 90 | effects[key] = val | ||
| 88 | self.deprecated = True | 91 | self.deprecated = True | ||
| 89 | other.deprecated = True | 92 | other.deprecated = True | ||
| 90 | return Potion(effects, duration, intensities) | 93 | return Potion(effects, duration, intensities) | ||
| 91 | 94 | ||||
| 92 | def __truediv__(self, other): | 95 | def __truediv__(self, other): | ||
| 93 | """Separation.""" | 96 | """Separation.""" | ||
| 94 | effects = {} | 97 | effects = {} | ||
| 95 | intensities = {} | 98 | intensities = {} | ||
| 96 | duration = self.duration | 99 | duration = self.duration | ||
| 97 | for key, val in self.effects.items(): | 100 | for key, val in self.effects.items(): | ||
| 98 | intensities[key] = self.round_down(self.intensities[key] / other) | 101 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
| 99 | effects[key] = val | 102 | effects[key] = val | ||
| 100 | self.deprecated = True | 103 | self.deprecated = True | ||
| 101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | 104 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | ||
| 102 | 105 | ||||
| 103 | def __eq__(self, other): | 106 | def __eq__(self, other): | ||
| 104 | """Equal.""" | 107 | """Equal.""" | ||
| 105 | return self.intensities == other.intensities | 108 | return self.intensities == other.intensities | ||
| 106 | 109 | ||||
| 107 | def __lt__(self, other): | 110 | def __lt__(self, other): | ||
| 108 | """Less than.""" | 111 | """Less than.""" | ||
| 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 112 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
| 110 | 113 | ||||
| 111 | 114 | ||||
| 112 | class ГоспожатаПоХимия: | 115 | class ГоспожатаПоХимия: | ||
| 113 | """Химичка Димитричка.""" | 116 | """Химичка Димитричка.""" | ||
| 114 | 117 | ||||
| 115 | def __init__(self): | 118 | def __init__(self): | ||
| 116 | """Initializator.""" | 119 | """Initializator.""" | ||
| n | 117 | self._cache = {} | n | 120 | self._target_cache = {} |
| 121 | self._potions_stack = {} | ||||
| 118 | 122 | ||||
| n | 119 | def _cache_target(self, target, duration): | n | 123 | def _cache_target(self, target): |
| 120 | """Cache all public properties of a target for restoring after a given duration.""" | 124 | """Cache all public properties of a target.""" | ||
| 121 | targets_cache = self._cache.setdefault(duration, {}) | 125 | if target in self._target_cache: | ||
| 122 | target_cache = targets_cache.setdefault(target, {}) | 126 | return False | ||
| 127 | self._target_cache[target] = {} | ||||
| 123 | for key, val in vars(target).items(): | 128 | for key, val in vars(target).items(): | ||
| 124 | if key.startswith('_'): | 129 | if key.startswith('_'): | ||
| 125 | continue | 130 | continue | ||
| n | 126 | target_cache[key] = copy.copy(val) | n | 131 | self._target_cache[target][key] = copy.deepcopy(val) |
| 127 | 132 | ||||
| n | 128 | def apply(self, target, potion): | n | 133 | def _apply_potion(self, target, potion): |
| 129 | """Apply a potion to a target.""" | 134 | """Apply a potion to a target.""" | ||
| n | n | 135 | potion_copy = copy.deepcopy(potion) | ||
| 130 | sorted_effects = sorted(potion.effects.keys(), | 136 | sorted_effects = sorted(potion.effects.keys(), | ||
| 131 | key=lambda x: sum(map(ord, x)), | 137 | key=lambda x: sum(map(ord, x)), | ||
| 132 | reverse=True) | 138 | reverse=True) | ||
| n | 133 | self._cache_target(target, potion.duration) | n | ||
| 134 | for key in sorted_effects: | 139 | for key in sorted_effects: | ||
| 135 | try: | 140 | try: | ||
| 136 | getattr(potion, key)(target) | 141 | getattr(potion, key)(target) | ||
| 137 | except TypeError: | 142 | except TypeError: | ||
| 138 | pass # This effect is not available | 143 | pass # This effect is not available | ||
| 139 | potion.depleted = True | 144 | potion.depleted = True | ||
| n | n | 145 | return potion_copy | ||
| 146 | |||||
| 147 | def _apply_all_potions(self, target): | ||||
| 148 | """Apply all potions to a target one by one.""" | ||||
| 149 | # Set initial state | ||||
| 150 | for attr_key, attr_val in self._target_cache[target].items(): | ||||
| 151 | setattr(target, attr_key, copy.deepcopy(attr_val)) | ||||
| 152 | # Apply all active potions | ||||
| 153 | # and replace the used potion with a potion ready to be used again | ||||
| 154 | if target in self._potions_stack: | ||||
| 155 | for i, potion in enumerate(self._potions_stack[target]): | ||||
| 156 | potion_copy = self._apply_potion(target, potion) | ||||
| 157 | self._potions_stack[target][i] = potion_copy | ||||
| 158 | |||||
| 159 | def apply(self, target, potion): | ||||
| 160 | """Apply a potion to a target.""" | ||||
| 161 | # Ensure potion is not depleted by trying to get an attribute | ||||
| 162 | # Depleted potion will raise a TypeError if it is depleted | ||||
| 163 | potion.depleted | ||||
| 164 | self._cache_target(target) | ||||
| 165 | self._potions_stack.setdefault(target, []).append(potion) | ||||
| 166 | self._apply_all_potions(target) | ||||
| 140 | 167 | ||||
| 141 | def tick(self): | 168 | def tick(self): | ||
| 142 | """Tick a turn.""" | 169 | """Tick a turn.""" | ||
| t | 143 | new_cache = {} | t | 170 | for target, potion_stack in self._potions_stack.items(): |
| 144 | for dur_key, dur_val in self._cache.items(): | 171 | new_potion_stack = [] | ||
| 145 | if not (ticked_dur_key := dur_key - 1): | 172 | for potion in potion_stack: | ||
| 146 | for target_key, target_val in dur_val.items(): | 173 | potion.duration -= 1 | ||
| 147 | for attr_key, attr_val in target_val.items(): | 174 | if potion.duration > 0: | ||
| 148 | setattr(target_key, attr_key, attr_val) | 175 | new_potion_stack.append(potion) | ||
| 149 | else: | 176 | self._potions_stack[target] = new_potion_stack | ||
| 150 | new_cache[ticked_dur_key] = dur_val | 177 | self._apply_all_potions(target) | ||
| 151 | self._cache = new_cache | 178 | if not len(new_potion_stack): | ||
| 179 | del self._target_cache[target] | ||||
| 180 | self._potions_stack = {key: val for key, val in self._potions_stack.items() if val} | ||||
| 181 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import copy | f | 1 | import copy |
| 2 | 2 | ||||
| 3 | 3 | ||||
| 4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
| 5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
| 6 | 6 | ||||
| 7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
| 8 | """Initializator.""" | 8 | """Initializator.""" | ||
| 9 | self.function = function | 9 | self.function = function | ||
| 10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
| 11 | self.depleted = False | 11 | self.depleted = False | ||
| 12 | 12 | ||||
| 13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
| 14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
| 15 | if self.depleted: | 15 | if self.depleted: | ||
| 16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
| 17 | self.depleted = True | 17 | self.depleted = True | ||
| 18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
| 19 | self.function(target) | 19 | self.function(target) | ||
| 20 | 20 | ||||
| 21 | 21 | ||||
| 22 | class Potion: | 22 | class Potion: | ||
| 23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
| 24 | 24 | ||||
| 25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
| 26 | """Initializator.""" | 26 | """Initializator.""" | ||
| 27 | self.effects = effects | 27 | self.effects = effects | ||
| 28 | self.duration = duration | 28 | self.duration = duration | ||
| 29 | self.deprecated = False | 29 | self.deprecated = False | ||
| 30 | self.depleted = False | 30 | self.depleted = False | ||
| 31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
| 32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
| 33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
| 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
| 35 | 35 | ||||
| 36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
| 37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
| 38 | if object.__getattribute__(self, 'depleted'): | 38 | if object.__getattribute__(self, 'depleted'): | ||
| 39 | raise TypeError("Potion is depleted.") | 39 | raise TypeError("Potion is depleted.") | ||
| 40 | if object.__getattribute__(self, 'deprecated'): | 40 | if object.__getattribute__(self, 'deprecated'): | ||
| 41 | raise TypeError("Potion is now part of something bigger than itself.") | 41 | raise TypeError("Potion is now part of something bigger than itself.") | ||
| 42 | return object.__getattribute__(self, name) | 42 | return object.__getattribute__(self, name) | ||
| 43 | 43 | ||||
| 44 | @staticmethod | 44 | @staticmethod | ||
| 45 | def round_down(val): | 45 | def round_down(val): | ||
| 46 | """Round x.5 down, other cases - as normal.""" | 46 | """Round x.5 down, other cases - as normal.""" | ||
| 47 | if val % 1 <= 0.5: | 47 | if val % 1 <= 0.5: | ||
| 48 | return int(val) | 48 | return int(val) | ||
| 49 | return int(val) + 1 | 49 | return int(val) + 1 | ||
| 50 | 50 | ||||
| 51 | def __add__(self, other): | 51 | def __add__(self, other): | ||
| 52 | """Combinations.""" | 52 | """Combinations.""" | ||
| 53 | effects = {} | 53 | effects = {} | ||
| 54 | intensities = {} | 54 | intensities = {} | ||
| 55 | duration = max(self.duration, other.duration) | 55 | duration = max(self.duration, other.duration) | ||
| 56 | for key, val in self.effects.items(): | 56 | for key, val in self.effects.items(): | ||
| 57 | intensities[key] = self.intensities[key] | 57 | intensities[key] = self.intensities[key] | ||
| 58 | effects[key] = val | 58 | effects[key] = val | ||
| 59 | for key, val in other.effects.items(): | 59 | for key, val in other.effects.items(): | ||
| 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
| 61 | effects[key] = val | 61 | effects[key] = val | ||
| 62 | self.deprecated = True | 62 | self.deprecated = True | ||
| 63 | other.deprecated = True | 63 | other.deprecated = True | ||
| 64 | return Potion(effects, duration, intensities) | 64 | return Potion(effects, duration, intensities) | ||
| 65 | 65 | ||||
| 66 | def __mul__(self, other): | 66 | def __mul__(self, other): | ||
| 67 | """Potentiation/Dilution.""" | 67 | """Potentiation/Dilution.""" | ||
| 68 | effects = {} | 68 | effects = {} | ||
| 69 | intensities = {} | 69 | intensities = {} | ||
| 70 | duration = self.duration | 70 | duration = self.duration | ||
| 71 | for key, val in self.effects.items(): | 71 | for key, val in self.effects.items(): | ||
| 72 | intensities[key] = self.round_down(self.intensities[key] * other) | 72 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
| 73 | effects[key] = val | 73 | effects[key] = val | ||
| 74 | self.deprecated = True | 74 | self.deprecated = True | ||
| 75 | return Potion(effects, duration, intensities) | 75 | return Potion(effects, duration, intensities) | ||
| 76 | 76 | ||||
| 77 | def __sub__(self, other): | 77 | def __sub__(self, other): | ||
| 78 | """Purification.""" | 78 | """Purification.""" | ||
| 79 | effects = {} | 79 | effects = {} | ||
| 80 | intensities = {} | 80 | intensities = {} | ||
| 81 | duration = self.duration | 81 | duration = self.duration | ||
| 82 | if set(other.effects) - set(self.effects): | 82 | if set(other.effects) - set(self.effects): | ||
| 83 | raise TypeError('Effect mismatch.') | 83 | raise TypeError('Effect mismatch.') | ||
| 84 | for key, val in self.effects.items(): | 84 | for key, val in self.effects.items(): | ||
| t | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | t | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: |
| 86 | intensities[key] = intensity | 86 | intensities[key] = intensity | ||
| 87 | effects[key] = val | 87 | effects[key] = val | ||
| 88 | self.deprecated = True | 88 | self.deprecated = True | ||
| 89 | other.deprecated = True | 89 | other.deprecated = True | ||
| 90 | return Potion(effects, duration, intensities) | 90 | return Potion(effects, duration, intensities) | ||
| 91 | 91 | ||||
| 92 | def __truediv__(self, other): | 92 | def __truediv__(self, other): | ||
| 93 | """Separation.""" | 93 | """Separation.""" | ||
| 94 | effects = {} | 94 | effects = {} | ||
| 95 | intensities = {} | 95 | intensities = {} | ||
| 96 | duration = self.duration | 96 | duration = self.duration | ||
| 97 | for key, val in self.effects.items(): | 97 | for key, val in self.effects.items(): | ||
| 98 | intensities[key] = self.round_down(self.intensities[key] / other) | 98 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
| 99 | effects[key] = val | 99 | effects[key] = val | ||
| 100 | self.deprecated = True | 100 | self.deprecated = True | ||
| 101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | 101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | ||
| 102 | 102 | ||||
| 103 | def __eq__(self, other): | 103 | def __eq__(self, other): | ||
| 104 | """Equal.""" | 104 | """Equal.""" | ||
| 105 | return self.intensities == other.intensities | 105 | return self.intensities == other.intensities | ||
| 106 | 106 | ||||
| 107 | def __lt__(self, other): | 107 | def __lt__(self, other): | ||
| 108 | """Less than.""" | 108 | """Less than.""" | ||
| 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
| 110 | 110 | ||||
| 111 | 111 | ||||
| 112 | class ГоспожатаПоХимия: | 112 | class ГоспожатаПоХимия: | ||
| 113 | """Химичка Димитричка.""" | 113 | """Химичка Димитричка.""" | ||
| 114 | 114 | ||||
| 115 | def __init__(self): | 115 | def __init__(self): | ||
| 116 | """Initializator.""" | 116 | """Initializator.""" | ||
| 117 | self._cache = {} | 117 | self._cache = {} | ||
| 118 | 118 | ||||
| 119 | def _cache_target(self, target, duration): | 119 | def _cache_target(self, target, duration): | ||
| 120 | """Cache all public properties of a target for restoring after a given duration.""" | 120 | """Cache all public properties of a target for restoring after a given duration.""" | ||
| 121 | targets_cache = self._cache.setdefault(duration, {}) | 121 | targets_cache = self._cache.setdefault(duration, {}) | ||
| 122 | target_cache = targets_cache.setdefault(target, {}) | 122 | target_cache = targets_cache.setdefault(target, {}) | ||
| 123 | for key, val in vars(target).items(): | 123 | for key, val in vars(target).items(): | ||
| 124 | if key.startswith('_'): | 124 | if key.startswith('_'): | ||
| 125 | continue | 125 | continue | ||
| 126 | target_cache[key] = copy.copy(val) | 126 | target_cache[key] = copy.copy(val) | ||
| 127 | 127 | ||||
| 128 | def apply(self, target, potion): | 128 | def apply(self, target, potion): | ||
| 129 | """Apply a potion to a target.""" | 129 | """Apply a potion to a target.""" | ||
| 130 | sorted_effects = sorted(potion.effects.keys(), | 130 | sorted_effects = sorted(potion.effects.keys(), | ||
| 131 | key=lambda x: sum(map(ord, x)), | 131 | key=lambda x: sum(map(ord, x)), | ||
| 132 | reverse=True) | 132 | reverse=True) | ||
| 133 | self._cache_target(target, potion.duration) | 133 | self._cache_target(target, potion.duration) | ||
| 134 | for key in sorted_effects: | 134 | for key in sorted_effects: | ||
| 135 | try: | 135 | try: | ||
| 136 | getattr(potion, key)(target) | 136 | getattr(potion, key)(target) | ||
| 137 | except TypeError: | 137 | except TypeError: | ||
| 138 | pass # This effect is not available | 138 | pass # This effect is not available | ||
| 139 | potion.depleted = True | 139 | potion.depleted = True | ||
| 140 | 140 | ||||
| 141 | def tick(self): | 141 | def tick(self): | ||
| 142 | """Tick a turn.""" | 142 | """Tick a turn.""" | ||
| 143 | new_cache = {} | 143 | new_cache = {} | ||
| 144 | for dur_key, dur_val in self._cache.items(): | 144 | for dur_key, dur_val in self._cache.items(): | ||
| 145 | if not (ticked_dur_key := dur_key - 1): | 145 | if not (ticked_dur_key := dur_key - 1): | ||
| 146 | for target_key, target_val in dur_val.items(): | 146 | for target_key, target_val in dur_val.items(): | ||
| 147 | for attr_key, attr_val in target_val.items(): | 147 | for attr_key, attr_val in target_val.items(): | ||
| 148 | setattr(target_key, attr_key, attr_val) | 148 | setattr(target_key, attr_key, attr_val) | ||
| 149 | else: | 149 | else: | ||
| 150 | new_cache[ticked_dur_key] = dur_val | 150 | new_cache[ticked_dur_key] = dur_val | ||
| 151 | self._cache = new_cache | 151 | self._cache = new_cache |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import copy | f | 1 | import copy |
| 2 | 2 | ||||
| 3 | 3 | ||||
| 4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
| 5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
| 6 | 6 | ||||
| 7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
| 8 | """Initializator.""" | 8 | """Initializator.""" | ||
| 9 | self.function = function | 9 | self.function = function | ||
| 10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
| 11 | self.depleted = False | 11 | self.depleted = False | ||
| 12 | 12 | ||||
| 13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
| 14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
| 15 | if self.depleted: | 15 | if self.depleted: | ||
| 16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
| 17 | self.depleted = True | 17 | self.depleted = True | ||
| 18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
| 19 | self.function(target) | 19 | self.function(target) | ||
| 20 | 20 | ||||
| 21 | 21 | ||||
| 22 | class Potion: | 22 | class Potion: | ||
| 23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
| 24 | 24 | ||||
| 25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
| 26 | """Initializator.""" | 26 | """Initializator.""" | ||
| 27 | self.effects = effects | 27 | self.effects = effects | ||
| 28 | self.duration = duration | 28 | self.duration = duration | ||
| 29 | self.deprecated = False | 29 | self.deprecated = False | ||
| 30 | self.depleted = False | 30 | self.depleted = False | ||
| 31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
| 32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
| 33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
| 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
| 35 | 35 | ||||
| 36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
| 37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
| 38 | if object.__getattribute__(self, 'depleted'): | 38 | if object.__getattribute__(self, 'depleted'): | ||
| 39 | raise TypeError("Potion is depleted.") | 39 | raise TypeError("Potion is depleted.") | ||
| 40 | if object.__getattribute__(self, 'deprecated'): | 40 | if object.__getattribute__(self, 'deprecated'): | ||
| 41 | raise TypeError("Potion is now part of something bigger than itself.") | 41 | raise TypeError("Potion is now part of something bigger than itself.") | ||
| 42 | return object.__getattribute__(self, name) | 42 | return object.__getattribute__(self, name) | ||
| 43 | 43 | ||||
| 44 | @staticmethod | 44 | @staticmethod | ||
| 45 | def round_down(val): | 45 | def round_down(val): | ||
| 46 | """Round x.5 down, other cases - as normal.""" | 46 | """Round x.5 down, other cases - as normal.""" | ||
| 47 | if val % 1 <= 0.5: | 47 | if val % 1 <= 0.5: | ||
| 48 | return int(val) | 48 | return int(val) | ||
| 49 | return int(val) + 1 | 49 | return int(val) + 1 | ||
| 50 | 50 | ||||
| 51 | def __add__(self, other): | 51 | def __add__(self, other): | ||
| 52 | """Combinations.""" | 52 | """Combinations.""" | ||
| 53 | effects = {} | 53 | effects = {} | ||
| 54 | intensities = {} | 54 | intensities = {} | ||
| 55 | duration = max(self.duration, other.duration) | 55 | duration = max(self.duration, other.duration) | ||
| 56 | for key, val in self.effects.items(): | 56 | for key, val in self.effects.items(): | ||
| 57 | intensities[key] = self.intensities[key] | 57 | intensities[key] = self.intensities[key] | ||
| 58 | effects[key] = val | 58 | effects[key] = val | ||
| 59 | for key, val in other.effects.items(): | 59 | for key, val in other.effects.items(): | ||
| 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
| 61 | effects[key] = val | 61 | effects[key] = val | ||
| 62 | self.deprecated = True | 62 | self.deprecated = True | ||
| 63 | other.deprecated = True | 63 | other.deprecated = True | ||
| 64 | return Potion(effects, duration, intensities) | 64 | return Potion(effects, duration, intensities) | ||
| 65 | 65 | ||||
| 66 | def __mul__(self, other): | 66 | def __mul__(self, other): | ||
| 67 | """Potentiation/Dilution.""" | 67 | """Potentiation/Dilution.""" | ||
| 68 | effects = {} | 68 | effects = {} | ||
| 69 | intensities = {} | 69 | intensities = {} | ||
| 70 | duration = self.duration | 70 | duration = self.duration | ||
| 71 | for key, val in self.effects.items(): | 71 | for key, val in self.effects.items(): | ||
| 72 | intensities[key] = self.round_down(self.intensities[key] * other) | 72 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
| 73 | effects[key] = val | 73 | effects[key] = val | ||
| 74 | self.deprecated = True | 74 | self.deprecated = True | ||
| 75 | return Potion(effects, duration, intensities) | 75 | return Potion(effects, duration, intensities) | ||
| 76 | 76 | ||||
| 77 | def __sub__(self, other): | 77 | def __sub__(self, other): | ||
| 78 | """Purification.""" | 78 | """Purification.""" | ||
| 79 | effects = {} | 79 | effects = {} | ||
| 80 | intensities = {} | 80 | intensities = {} | ||
| 81 | duration = self.duration | 81 | duration = self.duration | ||
| 82 | if set(other.effects) - set(self.effects): | 82 | if set(other.effects) - set(self.effects): | ||
| 83 | raise TypeError('Effect mismatch.') | 83 | raise TypeError('Effect mismatch.') | ||
| 84 | for key, val in self.effects.items(): | 84 | for key, val in self.effects.items(): | ||
| 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | ||
| 86 | intensities[key] = intensity | 86 | intensities[key] = intensity | ||
| 87 | effects[key] = val | 87 | effects[key] = val | ||
| 88 | self.deprecated = True | 88 | self.deprecated = True | ||
| 89 | other.deprecated = True | 89 | other.deprecated = True | ||
| 90 | return Potion(effects, duration, intensities) | 90 | return Potion(effects, duration, intensities) | ||
| 91 | 91 | ||||
| 92 | def __truediv__(self, other): | 92 | def __truediv__(self, other): | ||
| 93 | """Separation.""" | 93 | """Separation.""" | ||
| 94 | effects = {} | 94 | effects = {} | ||
| 95 | intensities = {} | 95 | intensities = {} | ||
| 96 | duration = self.duration | 96 | duration = self.duration | ||
| 97 | for key, val in self.effects.items(): | 97 | for key, val in self.effects.items(): | ||
| 98 | intensities[key] = self.round_down(self.intensities[key] / other) | 98 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
| 99 | effects[key] = val | 99 | effects[key] = val | ||
| 100 | self.deprecated = True | 100 | self.deprecated = True | ||
| t | 101 | return Potion(effects, duration, intensities) | t | 101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) |
| 102 | 102 | ||||
| 103 | def __eq__(self, other): | 103 | def __eq__(self, other): | ||
| 104 | """Equal.""" | 104 | """Equal.""" | ||
| 105 | return self.intensities == other.intensities | 105 | return self.intensities == other.intensities | ||
| 106 | 106 | ||||
| 107 | def __lt__(self, other): | 107 | def __lt__(self, other): | ||
| 108 | """Less than.""" | 108 | """Less than.""" | ||
| 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
| 110 | 110 | ||||
| 111 | 111 | ||||
| 112 | class ГоспожатаПоХимия: | 112 | class ГоспожатаПоХимия: | ||
| 113 | """Химичка Димитричка.""" | 113 | """Химичка Димитричка.""" | ||
| 114 | 114 | ||||
| 115 | def __init__(self): | 115 | def __init__(self): | ||
| 116 | """Initializator.""" | 116 | """Initializator.""" | ||
| 117 | self._cache = {} | 117 | self._cache = {} | ||
| 118 | 118 | ||||
| 119 | def _cache_target(self, target, duration): | 119 | def _cache_target(self, target, duration): | ||
| 120 | """Cache all public properties of a target for restoring after a given duration.""" | 120 | """Cache all public properties of a target for restoring after a given duration.""" | ||
| 121 | targets_cache = self._cache.setdefault(duration, {}) | 121 | targets_cache = self._cache.setdefault(duration, {}) | ||
| 122 | target_cache = targets_cache.setdefault(target, {}) | 122 | target_cache = targets_cache.setdefault(target, {}) | ||
| 123 | for key, val in vars(target).items(): | 123 | for key, val in vars(target).items(): | ||
| 124 | if key.startswith('_'): | 124 | if key.startswith('_'): | ||
| 125 | continue | 125 | continue | ||
| 126 | target_cache[key] = copy.copy(val) | 126 | target_cache[key] = copy.copy(val) | ||
| 127 | 127 | ||||
| 128 | def apply(self, target, potion): | 128 | def apply(self, target, potion): | ||
| 129 | """Apply a potion to a target.""" | 129 | """Apply a potion to a target.""" | ||
| 130 | sorted_effects = sorted(potion.effects.keys(), | 130 | sorted_effects = sorted(potion.effects.keys(), | ||
| 131 | key=lambda x: sum(map(ord, x)), | 131 | key=lambda x: sum(map(ord, x)), | ||
| 132 | reverse=True) | 132 | reverse=True) | ||
| 133 | self._cache_target(target, potion.duration) | 133 | self._cache_target(target, potion.duration) | ||
| 134 | for key in sorted_effects: | 134 | for key in sorted_effects: | ||
| 135 | try: | 135 | try: | ||
| 136 | getattr(potion, key)(target) | 136 | getattr(potion, key)(target) | ||
| 137 | except TypeError: | 137 | except TypeError: | ||
| 138 | pass # This effect is not available | 138 | pass # This effect is not available | ||
| 139 | potion.depleted = True | 139 | potion.depleted = True | ||
| 140 | 140 | ||||
| 141 | def tick(self): | 141 | def tick(self): | ||
| 142 | """Tick a turn.""" | 142 | """Tick a turn.""" | ||
| 143 | new_cache = {} | 143 | new_cache = {} | ||
| 144 | for dur_key, dur_val in self._cache.items(): | 144 | for dur_key, dur_val in self._cache.items(): | ||
| 145 | if not (ticked_dur_key := dur_key - 1): | 145 | if not (ticked_dur_key := dur_key - 1): | ||
| 146 | for target_key, target_val in dur_val.items(): | 146 | for target_key, target_val in dur_val.items(): | ||
| 147 | for attr_key, attr_val in target_val.items(): | 147 | for attr_key, attr_val in target_val.items(): | ||
| 148 | setattr(target_key, attr_key, attr_val) | 148 | setattr(target_key, attr_key, attr_val) | ||
| 149 | else: | 149 | else: | ||
| 150 | new_cache[ticked_dur_key] = dur_val | 150 | new_cache[ticked_dur_key] = dur_val | ||
| 151 | self._cache = new_cache | 151 | self._cache = new_cache |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import copy | f | 1 | import copy |
| 2 | 2 | ||||
| 3 | 3 | ||||
| 4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
| 5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
| 6 | 6 | ||||
| 7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
| 8 | """Initializator.""" | 8 | """Initializator.""" | ||
| 9 | self.function = function | 9 | self.function = function | ||
| 10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
| 11 | self.depleted = False | 11 | self.depleted = False | ||
| 12 | 12 | ||||
| 13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
| 14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
| 15 | if self.depleted: | 15 | if self.depleted: | ||
| 16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
| 17 | self.depleted = True | 17 | self.depleted = True | ||
| 18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
| 19 | self.function(target) | 19 | self.function(target) | ||
| 20 | 20 | ||||
| 21 | 21 | ||||
| 22 | class Potion: | 22 | class Potion: | ||
| 23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
| 24 | 24 | ||||
| 25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
| 26 | """Initializator.""" | 26 | """Initializator.""" | ||
| 27 | self.effects = effects | 27 | self.effects = effects | ||
| 28 | self.duration = duration | 28 | self.duration = duration | ||
| 29 | self.deprecated = False | 29 | self.deprecated = False | ||
| 30 | self.depleted = False | 30 | self.depleted = False | ||
| 31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
| 32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
| 33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
| 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
| 35 | 35 | ||||
| 36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
| 37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
| 38 | if object.__getattribute__(self, 'depleted'): | 38 | if object.__getattribute__(self, 'depleted'): | ||
| 39 | raise TypeError("Potion is depleted.") | 39 | raise TypeError("Potion is depleted.") | ||
| 40 | if object.__getattribute__(self, 'deprecated'): | 40 | if object.__getattribute__(self, 'deprecated'): | ||
| 41 | raise TypeError("Potion is now part of something bigger than itself.") | 41 | raise TypeError("Potion is now part of something bigger than itself.") | ||
| 42 | return object.__getattribute__(self, name) | 42 | return object.__getattribute__(self, name) | ||
| 43 | 43 | ||||
| 44 | @staticmethod | 44 | @staticmethod | ||
| 45 | def round_down(val): | 45 | def round_down(val): | ||
| 46 | """Round x.5 down, other cases - as normal.""" | 46 | """Round x.5 down, other cases - as normal.""" | ||
| 47 | if val % 1 <= 0.5: | 47 | if val % 1 <= 0.5: | ||
| 48 | return int(val) | 48 | return int(val) | ||
| 49 | return int(val) + 1 | 49 | return int(val) + 1 | ||
| 50 | 50 | ||||
| 51 | def __add__(self, other): | 51 | def __add__(self, other): | ||
| 52 | """Combinations.""" | 52 | """Combinations.""" | ||
| 53 | effects = {} | 53 | effects = {} | ||
| 54 | intensities = {} | 54 | intensities = {} | ||
| 55 | duration = max(self.duration, other.duration) | 55 | duration = max(self.duration, other.duration) | ||
| 56 | for key, val in self.effects.items(): | 56 | for key, val in self.effects.items(): | ||
| 57 | intensities[key] = self.intensities[key] | 57 | intensities[key] = self.intensities[key] | ||
| 58 | effects[key] = val | 58 | effects[key] = val | ||
| 59 | for key, val in other.effects.items(): | 59 | for key, val in other.effects.items(): | ||
| 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
| 61 | effects[key] = val | 61 | effects[key] = val | ||
| 62 | self.deprecated = True | 62 | self.deprecated = True | ||
| 63 | other.deprecated = True | 63 | other.deprecated = True | ||
| 64 | return Potion(effects, duration, intensities) | 64 | return Potion(effects, duration, intensities) | ||
| 65 | 65 | ||||
| 66 | def __mul__(self, other): | 66 | def __mul__(self, other): | ||
| 67 | """Potentiation/Dilution.""" | 67 | """Potentiation/Dilution.""" | ||
| 68 | effects = {} | 68 | effects = {} | ||
| 69 | intensities = {} | 69 | intensities = {} | ||
| 70 | duration = self.duration | 70 | duration = self.duration | ||
| 71 | for key, val in self.effects.items(): | 71 | for key, val in self.effects.items(): | ||
| 72 | intensities[key] = self.round_down(self.intensities[key] * other) | 72 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
| 73 | effects[key] = val | 73 | effects[key] = val | ||
| 74 | self.deprecated = True | 74 | self.deprecated = True | ||
| 75 | return Potion(effects, duration, intensities) | 75 | return Potion(effects, duration, intensities) | ||
| 76 | 76 | ||||
| 77 | def __sub__(self, other): | 77 | def __sub__(self, other): | ||
| 78 | """Purification.""" | 78 | """Purification.""" | ||
| 79 | effects = {} | 79 | effects = {} | ||
| 80 | intensities = {} | 80 | intensities = {} | ||
| 81 | duration = self.duration | 81 | duration = self.duration | ||
| 82 | if set(other.effects) - set(self.effects): | 82 | if set(other.effects) - set(self.effects): | ||
| 83 | raise TypeError('Effect mismatch.') | 83 | raise TypeError('Effect mismatch.') | ||
| 84 | for key, val in self.effects.items(): | 84 | for key, val in self.effects.items(): | ||
| 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | ||
| 86 | intensities[key] = intensity | 86 | intensities[key] = intensity | ||
| 87 | effects[key] = val | 87 | effects[key] = val | ||
| 88 | self.deprecated = True | 88 | self.deprecated = True | ||
| 89 | other.deprecated = True | 89 | other.deprecated = True | ||
| 90 | return Potion(effects, duration, intensities) | 90 | return Potion(effects, duration, intensities) | ||
| 91 | 91 | ||||
| 92 | def __truediv__(self, other): | 92 | def __truediv__(self, other): | ||
| 93 | """Separation.""" | 93 | """Separation.""" | ||
| 94 | effects = {} | 94 | effects = {} | ||
| 95 | intensities = {} | 95 | intensities = {} | ||
| 96 | duration = self.duration | 96 | duration = self.duration | ||
| 97 | for key, val in self.effects.items(): | 97 | for key, val in self.effects.items(): | ||
| 98 | intensities[key] = self.round_down(self.intensities[key] / other) | 98 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
| 99 | effects[key] = val | 99 | effects[key] = val | ||
| 100 | self.deprecated = True | 100 | self.deprecated = True | ||
| 101 | return Potion(effects, duration, intensities) | 101 | return Potion(effects, duration, intensities) | ||
| 102 | 102 | ||||
| 103 | def __eq__(self, other): | 103 | def __eq__(self, other): | ||
| 104 | """Equal.""" | 104 | """Equal.""" | ||
| 105 | return self.intensities == other.intensities | 105 | return self.intensities == other.intensities | ||
| 106 | 106 | ||||
| 107 | def __lt__(self, other): | 107 | def __lt__(self, other): | ||
| 108 | """Less than.""" | 108 | """Less than.""" | ||
| 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
| 110 | 110 | ||||
| 111 | 111 | ||||
| 112 | class ГоспожатаПоХимия: | 112 | class ГоспожатаПоХимия: | ||
| 113 | """Химичка Димитричка.""" | 113 | """Химичка Димитричка.""" | ||
| 114 | 114 | ||||
| 115 | def __init__(self): | 115 | def __init__(self): | ||
| 116 | """Initializator.""" | 116 | """Initializator.""" | ||
| 117 | self._cache = {} | 117 | self._cache = {} | ||
| 118 | 118 | ||||
| 119 | def _cache_target(self, target, duration): | 119 | def _cache_target(self, target, duration): | ||
| 120 | """Cache all public properties of a target for restoring after a given duration.""" | 120 | """Cache all public properties of a target for restoring after a given duration.""" | ||
| 121 | targets_cache = self._cache.setdefault(duration, {}) | 121 | targets_cache = self._cache.setdefault(duration, {}) | ||
| 122 | target_cache = targets_cache.setdefault(target, {}) | 122 | target_cache = targets_cache.setdefault(target, {}) | ||
| 123 | for key, val in vars(target).items(): | 123 | for key, val in vars(target).items(): | ||
| 124 | if key.startswith('_'): | 124 | if key.startswith('_'): | ||
| 125 | continue | 125 | continue | ||
| 126 | target_cache[key] = copy.copy(val) | 126 | target_cache[key] = copy.copy(val) | ||
| 127 | 127 | ||||
| 128 | def apply(self, target, potion): | 128 | def apply(self, target, potion): | ||
| 129 | """Apply a potion to a target.""" | 129 | """Apply a potion to a target.""" | ||
| 130 | sorted_effects = sorted(potion.effects.keys(), | 130 | sorted_effects = sorted(potion.effects.keys(), | ||
| 131 | key=lambda x: sum(map(ord, x)), | 131 | key=lambda x: sum(map(ord, x)), | ||
| 132 | reverse=True) | 132 | reverse=True) | ||
| 133 | self._cache_target(target, potion.duration) | 133 | self._cache_target(target, potion.duration) | ||
| 134 | for key in sorted_effects: | 134 | for key in sorted_effects: | ||
| 135 | try: | 135 | try: | ||
| 136 | getattr(potion, key)(target) | 136 | getattr(potion, key)(target) | ||
| 137 | except TypeError: | 137 | except TypeError: | ||
| 138 | pass # This effect is not available | 138 | pass # This effect is not available | ||
| 139 | potion.depleted = True | 139 | potion.depleted = True | ||
| 140 | 140 | ||||
| 141 | def tick(self): | 141 | def tick(self): | ||
| 142 | """Tick a turn.""" | 142 | """Tick a turn.""" | ||
| 143 | new_cache = {} | 143 | new_cache = {} | ||
| 144 | for dur_key, dur_val in self._cache.items(): | 144 | for dur_key, dur_val in self._cache.items(): | ||
| 145 | if not (ticked_dur_key := dur_key - 1): | 145 | if not (ticked_dur_key := dur_key - 1): | ||
| 146 | for target_key, target_val in dur_val.items(): | 146 | for target_key, target_val in dur_val.items(): | ||
| 147 | for attr_key, attr_val in target_val.items(): | 147 | for attr_key, attr_val in target_val.items(): | ||
| 148 | setattr(target_key, attr_key, attr_val) | 148 | setattr(target_key, attr_key, attr_val) | ||
| 149 | else: | 149 | else: | ||
| 150 | new_cache[ticked_dur_key] = dur_val | 150 | new_cache[ticked_dur_key] = dur_val | ||
| 151 | self._cache = new_cache | 151 | self._cache = new_cache | ||
| t | 152 | t | |||
| 153 | |||||
| 154 | |||||
| 155 | |||||
| 156 | |||||
| 157 | |||||
| 158 | |||||
| 159 | |||||
| 160 | if __name__ == '__main__': | ||||
| 161 | |||||
| 162 | class Target: | ||||
| 163 | |||||
| 164 | def __init__(self): | ||||
| 165 | self.size = 5 | ||||
| 166 | |||||
| 167 | target = Target() | ||||
| 168 | |||||
| 169 | narco = ГоспожатаПоХимия() | ||||
| 170 | effects = {'grow': lambda target: setattr(target, 'size', target.size*2)} | ||||
| 171 | grow_potion1 = Potion(effects, duration=2) | ||||
| 172 | grow_potion2 = Potion(effects, duration=2) | ||||
| 173 | print(target.size) | ||||
| 174 | narco.apply(target, grow_potion1) | ||||
| 175 | print(target.size) | ||||
| 176 | narco.tick() | ||||
| 177 | print(target.size) | ||||
| 178 | narco.tick() | ||||
| 179 | print(target.size) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
04.12.2023 15:14
05.12.2023 14:34
04.12.2023 15:15