1import cmath
2
3EFFECT_ERROR_TEXT = "Effect is depleted."
4POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself."
5INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution"
6POTION_IS_USED_ERROR_TEXT = "Potion is depleted."
7
8
9class PotionEffect:
10 @staticmethod
11 def custom_round(value):
12 result = round(value)
13 if cmath.isclose(
14 result - value, 0.5
15 ): # the edge case where x.5 should be rounded down
16 result -= 1
17 return result
18
19 def __init__(self, func):
20 self.func = func
21 self.times = 1
22 self.used = False
23
24 def __iadd__(self, other):
25 self.times += other.times
26 return self
27
28 def __isub__(self, other):
29 self.times -= other.times
30 return self
31
32 def __imul__(self, other):
33 self.times = PotionEffect.custom_round(self.times * other)
34 return self
35
36 def __itruediv__(self, other):
37 self.times = PotionEffect.custom_round(self.times / other)
38 return self
39
40 def copy(self):
41 result = PotionEffect(self.func)
42 result.times = self.times
43 result.used = self.used
44 return result
45
46 def __call__(self, target):
47 if self.used:
48 raise TypeError(EFFECT_ERROR_TEXT)
49 for _ in range(self.times):
50 self.func(target)
51 self.used = True
52
53
54class meta(type):
55 def __new__(cls, name, bases, attr_dict):
56 return type.__new__(cls, name, bases, attr_dict)
57
58
59class Potion(metaclass=meta):
60 @staticmethod
61 def fake_func_for_component(target):
62 raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT)
63
64 @staticmethod
65 def update_dict_with_class_funcs(result_dict):
66 result_dict.update(
67 {key: value for key, value in vars(Potion).items() if not key in dir(type)}
68 )
69 result_dict["__init__"] = Potion.__init__
70 result_dict["__eq__"] = Potion.__eq__
71 result_dict["__lt__"] = Potion.__lt__
72 result_dict["__gt__"] = Potion.__gt__
73 result_dict.pop("__weakref__")
74 return result_dict
75
76 def validate_state_on_operation(self):
77 if self.is_component:
78 raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT)
79 if self.is_used():
80 raise TypeError(POTION_IS_USED_ERROR_TEXT)
81
82 def __init__(self, effects, duration):
83 for key, value in effects.items():
84 setattr(self, key, PotionEffect(value))
85 self.duration = duration
86 self.is_component = False
87
88 def is_used(self):
89 for key in dir(self):
90 value = getattr(self, key)
91 if type(value) is PotionEffect and not value.used:
92 return False
93 return True
94
95 def __getattribute__(self, name):
96 result = object.__getattribute__(self, name)
97 if (
98 object.__getattribute__(self, "is_component")
99 and type(result) is PotionEffect
100 ):
101 fake_result = PotionEffect(Potion.fake_func_for_component)
102 fake_result.times = result.times
103 fake_result.used = result.used
104 return fake_result
105
106 return result
107
108 def __add__(self, other):
109 self.validate_state_on_operation()
110 other.validate_state_on_operation()
111 # transfering the methods from the object on the left side to dict,
112 # and if there are matching methods in the right side, we increace the intensity
113 result_dict = {}
114 for key in dir(self):
115 value = getattr(self, key)
116 if type(value) is PotionEffect and not value.used:
117 result_dict[key] = value.copy()
118 if key in dir(other):
119 other_value = getattr(other, key)
120 if not other_value.used:
121 result_dict[key] += other_value
122
123 # transfering the methods that are only in the right side to the dict
124 for key in dir(other):
125 value = getattr(other, key)
126 if (
127 type(value) is PotionEffect
128 and not value.used
129 and not key in result_dict.keys()
130 ):
131 result_dict[key] = getattr(other, key).copy()
132
133 Potion.update_dict_with_class_funcs(result_dict)
134 result = type("Potion", (object,), result_dict)(
135 {}, max(self.duration, other.duration)
136 )
137 self.is_component = True
138 other.is_component = True
139
140 return result
141
142 def __mul__(self, other):
143 self.validate_state_on_operation()
144 result_dict = {}
145 # transfering the methods from the object to the dict and multiply them with the number
146 for key in dir(self):
147 value = getattr(self, key)
148 if type(value) is PotionEffect and not value.used:
149 result_dict[key] = value.copy()
150 result_dict[key] *= other
151
152 Potion.update_dict_with_class_funcs(result_dict)
153
154 result = meta("Potion", (object,), result_dict)({}, self.duration)
155 self.is_component = True
156
157 return result
158
159 def __sub__(self, other):
160 self.validate_state_on_operation()
161 other.validate_state_on_operation()
162 result_dict = {}
163 # validation that the substitution is valid
164 for key in dir(other):
165 value = getattr(other, key)
166 if type(value) is PotionEffect and not value.used and not key in dir(self):
167 raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT)
168 # transfering the methods from the object on the left side to dict,
169 # and if there are matching methods in the right side, we subtract their intensity
170 for key in dir(self):
171 value = getattr(self, key)
172 if not type(value) is PotionEffect or value.used:
173 continue
174 result_dict[key] = value.copy()
175 if key in dir(other):
176 other_value = getattr(other, key)
177 if not other_value.used:
178 result_dict[key] -= getattr(other, key)
179 if result_dict[key].times <= 0:
180 result_dict.pop(key)
181
182 Potion.update_dict_with_class_funcs(result_dict)
183 result = meta("Potion", (object,), result_dict)({}, self.duration)
184 self.is_component = True
185 other.is_component = True
186 return result
187
188 def __truediv__(self, other):
189 self.validate_state_on_operation()
190 # spliting the object into equal parts, where the intensity is devided by the number
191 result_list = []
192 for _ in range(other):
193 result_dict = {}
194 for key in dir(self):
195 value = getattr(self, key)
196 if type(value) is PotionEffect and not value.used:
197 result_dict[key] = value.copy()
198 result_dict[key] /= other
199
200 Potion.update_dict_with_class_funcs(result_dict)
201 result = meta("Potion", (object,), result_dict)({}, self.duration)
202 result_list.append(result)
203
204 self.is_component = True
205 return tuple(result_list)
206
207 def __eq__(self, other):
208 self.validate_state_on_operation()
209 other.validate_state_on_operation()
210 for key in dir(self):
211 value = getattr(self, key)
212 if type(value) is PotionEffect and not value.used:
213 if not key in dir(other):
214 return False
215 other_value = getattr(other, key)
216 if value.times != other_value.times or value.used != other_value.used:
217 return False
218
219 for key in dir(other):
220 value = getattr(other, key)
221 if type(value) is PotionEffect and not value.used and not key in dir(self):
222 return False
223
224 return True
225
226 def get_sum(self):
227 result = 0
228 for key in dir(self):
229 value = getattr(self, key)
230 if type(value) is PotionEffect and not value.used:
231 result += value.times
232 return result
233
234 def __gt__(self, other):
235 self.validate_state_on_operation()
236 other.validate_state_on_operation()
237 return self.get_sum() > other.get_sum()
238
239 def __lt__(self, other):
240 self.validate_state_on_operation()
241 other.validate_state_on_operation()
242 return self.get_sum() < other.get_sum()
243
244
245class ГоспожатаПоХимия:
246 @staticmethod
247 def calculate_molecular_mass(element):
248 return sum([ord(i) for i in element])
249
250 def __init__(self):
251 # dictionary to store the reverse effectss of potions as a key, and when to be executed as values
252 self.timer = {}
253
254 def apply_effect(self, target, effect):
255 # creating a revrse function to reverse the changes after the effect expires
256 old_attributes = vars(target).copy()
257 effect(target)
258 mid__attributes = vars(target).copy()
259
260 def reverse_effect():
261 new_attributes = vars(target).copy()
262 for key, value in new_attributes.items():
263 if not key in mid__attributes.keys():
264 continue
265 is_old = key in old_attributes.keys()
266 if type(value) in (int, float):
267 old_val = 0
268 if is_old:
269 old_val = old_attributes[key]
270
271 if (
272 value - (mid__attributes[key] - old_val)
273 ) == old_val and not is_old:
274 delattr(target, key)
275 else:
276 setattr(target, key, value - (mid__attributes[key] - old_val))
277 elif value == mid__attributes[key] and key in old_attributes.keys():
278 setattr(target, key, old_attributes[key])
279 elif not is_old:
280 delattr(target, key)
281
282 return reverse_effect
283
284 def apply(self, target, potion):
285 if potion.is_used():
286 raise TypeError(POTION_IS_USED_ERROR_TEXT)
287
288 if potion.is_component: # this is probably useless, but just to be sure
289 raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT)
290
291 if potion.duration == 0:
292 return
293 ordered_dir = sorted(
294 dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True
295 )
296 for key in ordered_dir:
297 value = getattr(potion, key)
298 if type(value) is PotionEffect and not value.used:
299 reverse_func = self.apply_effect(target, value)
300 self.timer[reverse_func] = potion.duration
301
302 def tick(self):
303 to_iterate = self.timer.copy().items()
304 for key, value in to_iterate:
305 self.timer[key] -= 1
306 if value <= 1:
307 key()
308 self.timer.pop(key)
.................F.F
======================================================================
FAIL: test_ticking_multiple_potions (test.TestГоспожатаПоХимия)
Test ticking after applying multiple potions which affect the same attribute.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 458, in test_ticking_multiple_potions
self.assertEqual(self._target.int_attr, 50)
AssertionError: 455 != 50
======================================================================
FAIL: test_ticking_mutable (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with mutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 446, in test_ticking_mutable
self.assertEqual(self._target.list_attr, [1, 2, 3])
AssertionError: Lists differ: [1, 2, 3, 4] != [1, 2, 3]
First list contains 1 additional elements.
First extra element 3:
4
- [1, 2, 3, 4]
? ---
+ [1, 2, 3]
----------------------------------------------------------------------
Ran 20 tests in 0.010s
FAILED (failures=2)
| n | n | 1 | |||
| 1 | import cmath | 2 | import cmath | ||
| 2 | 3 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 4 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 5 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 6 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 7 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 8 | ||||
| 8 | 9 | ||||
| 9 | class PotionEffect: | 10 | class PotionEffect: | ||
| 10 | @staticmethod | 11 | @staticmethod | ||
| 11 | def custom_round(value): | 12 | def custom_round(value): | ||
| 12 | result = round(value) | 13 | result = round(value) | ||
| 13 | if cmath.isclose( | 14 | if cmath.isclose( | ||
| 14 | result - value, 0.5 | 15 | result - value, 0.5 | ||
| 15 | ): # the edge case where x.5 should be rounded down | 16 | ): # the edge case where x.5 should be rounded down | ||
| 16 | result -= 1 | 17 | result -= 1 | ||
| 17 | return result | 18 | return result | ||
| 18 | 19 | ||||
| 19 | def __init__(self, func): | 20 | def __init__(self, func): | ||
| 20 | self.func = func | 21 | self.func = func | ||
| 21 | self.times = 1 | 22 | self.times = 1 | ||
| 22 | self.used = False | 23 | self.used = False | ||
| 23 | 24 | ||||
| 24 | def __iadd__(self, other): | 25 | def __iadd__(self, other): | ||
| 25 | self.times += other.times | 26 | self.times += other.times | ||
| 26 | return self | 27 | return self | ||
| 27 | 28 | ||||
| 28 | def __isub__(self, other): | 29 | def __isub__(self, other): | ||
| 29 | self.times -= other.times | 30 | self.times -= other.times | ||
| 30 | return self | 31 | return self | ||
| 31 | 32 | ||||
| 32 | def __imul__(self, other): | 33 | def __imul__(self, other): | ||
| 33 | self.times = PotionEffect.custom_round(self.times * other) | 34 | self.times = PotionEffect.custom_round(self.times * other) | ||
| 34 | return self | 35 | return self | ||
| 35 | 36 | ||||
| 36 | def __itruediv__(self, other): | 37 | def __itruediv__(self, other): | ||
| 37 | self.times = PotionEffect.custom_round(self.times / other) | 38 | self.times = PotionEffect.custom_round(self.times / other) | ||
| 38 | return self | 39 | return self | ||
| 39 | 40 | ||||
| 40 | def copy(self): | 41 | def copy(self): | ||
| 41 | result = PotionEffect(self.func) | 42 | result = PotionEffect(self.func) | ||
| 42 | result.times = self.times | 43 | result.times = self.times | ||
| 43 | result.used = self.used | 44 | result.used = self.used | ||
| 44 | return result | 45 | return result | ||
| 45 | 46 | ||||
| 46 | def __call__(self, target): | 47 | def __call__(self, target): | ||
| 47 | if self.used: | 48 | if self.used: | ||
| 48 | raise TypeError(EFFECT_ERROR_TEXT) | 49 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 49 | for _ in range(self.times): | 50 | for _ in range(self.times): | ||
| 50 | self.func(target) | 51 | self.func(target) | ||
| 51 | self.used = True | 52 | self.used = True | ||
| 52 | 53 | ||||
| 53 | 54 | ||||
| 54 | class meta(type): | 55 | class meta(type): | ||
| 55 | def __new__(cls, name, bases, attr_dict): | 56 | def __new__(cls, name, bases, attr_dict): | ||
| 56 | return type.__new__(cls, name, bases, attr_dict) | 57 | return type.__new__(cls, name, bases, attr_dict) | ||
| 57 | 58 | ||||
| 58 | 59 | ||||
| 59 | class Potion(metaclass=meta): | 60 | class Potion(metaclass=meta): | ||
| 60 | @staticmethod | 61 | @staticmethod | ||
| 61 | def fake_func_for_component(target): | 62 | def fake_func_for_component(target): | ||
| 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 63 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 63 | 64 | ||||
| 64 | @staticmethod | 65 | @staticmethod | ||
| 65 | def update_dict_with_class_funcs(result_dict): | 66 | def update_dict_with_class_funcs(result_dict): | ||
| 66 | result_dict.update( | 67 | result_dict.update( | ||
| 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | 68 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | ||
| 68 | ) | 69 | ) | ||
| 69 | result_dict["__init__"] = Potion.__init__ | 70 | result_dict["__init__"] = Potion.__init__ | ||
| 70 | result_dict["__eq__"] = Potion.__eq__ | 71 | result_dict["__eq__"] = Potion.__eq__ | ||
| 71 | result_dict["__lt__"] = Potion.__lt__ | 72 | result_dict["__lt__"] = Potion.__lt__ | ||
| 72 | result_dict["__gt__"] = Potion.__gt__ | 73 | result_dict["__gt__"] = Potion.__gt__ | ||
| 73 | result_dict.pop("__weakref__") | 74 | result_dict.pop("__weakref__") | ||
| 74 | return result_dict | 75 | return result_dict | ||
| 75 | 76 | ||||
| 76 | def validate_state_on_operation(self): | 77 | def validate_state_on_operation(self): | ||
| 77 | if self.is_component: | 78 | if self.is_component: | ||
| 78 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 79 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 79 | if self.is_used(): | 80 | if self.is_used(): | ||
| 80 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 81 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 81 | 82 | ||||
| 82 | def __init__(self, effects, duration): | 83 | def __init__(self, effects, duration): | ||
| 83 | for key, value in effects.items(): | 84 | for key, value in effects.items(): | ||
| 84 | setattr(self, key, PotionEffect(value)) | 85 | setattr(self, key, PotionEffect(value)) | ||
| 85 | self.duration = duration | 86 | self.duration = duration | ||
| 86 | self.is_component = False | 87 | self.is_component = False | ||
| 87 | 88 | ||||
| 88 | def is_used(self): | 89 | def is_used(self): | ||
| 89 | for key in dir(self): | 90 | for key in dir(self): | ||
| 90 | value = getattr(self, key) | 91 | value = getattr(self, key) | ||
| 91 | if type(value) is PotionEffect and not value.used: | 92 | if type(value) is PotionEffect and not value.used: | ||
| 92 | return False | 93 | return False | ||
| 93 | return True | 94 | return True | ||
| 94 | 95 | ||||
| 95 | def __getattribute__(self, name): | 96 | def __getattribute__(self, name): | ||
| 96 | result = object.__getattribute__(self, name) | 97 | result = object.__getattribute__(self, name) | ||
| 97 | if ( | 98 | if ( | ||
| 98 | object.__getattribute__(self, "is_component") | 99 | object.__getattribute__(self, "is_component") | ||
| 99 | and type(result) is PotionEffect | 100 | and type(result) is PotionEffect | ||
| 100 | ): | 101 | ): | ||
| 101 | fake_result = PotionEffect(Potion.fake_func_for_component) | 102 | fake_result = PotionEffect(Potion.fake_func_for_component) | ||
| 102 | fake_result.times = result.times | 103 | fake_result.times = result.times | ||
| 103 | fake_result.used = result.used | 104 | fake_result.used = result.used | ||
| 104 | return fake_result | 105 | return fake_result | ||
| 105 | 106 | ||||
| 106 | return result | 107 | return result | ||
| 107 | 108 | ||||
| 108 | def __add__(self, other): | 109 | def __add__(self, other): | ||
| 109 | self.validate_state_on_operation() | 110 | self.validate_state_on_operation() | ||
| 110 | other.validate_state_on_operation() | 111 | other.validate_state_on_operation() | ||
| 111 | # transfering the methods from the object on the left side to dict, | 112 | # transfering the methods from the object on the left side to dict, | ||
| 112 | # and if there are matching methods in the right side, we increace the intensity | 113 | # and if there are matching methods in the right side, we increace the intensity | ||
| 113 | result_dict = {} | 114 | result_dict = {} | ||
| 114 | for key in dir(self): | 115 | for key in dir(self): | ||
| 115 | value = getattr(self, key) | 116 | value = getattr(self, key) | ||
| 116 | if type(value) is PotionEffect and not value.used: | 117 | if type(value) is PotionEffect and not value.used: | ||
| 117 | result_dict[key] = value.copy() | 118 | result_dict[key] = value.copy() | ||
| 118 | if key in dir(other): | 119 | if key in dir(other): | ||
| 119 | other_value = getattr(other, key) | 120 | other_value = getattr(other, key) | ||
| 120 | if not other_value.used: | 121 | if not other_value.used: | ||
| 121 | result_dict[key] += other_value | 122 | result_dict[key] += other_value | ||
| 122 | 123 | ||||
| 123 | # transfering the methods that are only in the right side to the dict | 124 | # transfering the methods that are only in the right side to the dict | ||
| 124 | for key in dir(other): | 125 | for key in dir(other): | ||
| 125 | value = getattr(other, key) | 126 | value = getattr(other, key) | ||
| 126 | if ( | 127 | if ( | ||
| 127 | type(value) is PotionEffect | 128 | type(value) is PotionEffect | ||
| 128 | and not value.used | 129 | and not value.used | ||
| 129 | and not key in result_dict.keys() | 130 | and not key in result_dict.keys() | ||
| 130 | ): | 131 | ): | ||
| 131 | result_dict[key] = getattr(other, key).copy() | 132 | result_dict[key] = getattr(other, key).copy() | ||
| 132 | 133 | ||||
| 133 | Potion.update_dict_with_class_funcs(result_dict) | 134 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 134 | result = type("Potion", (object,), result_dict)( | 135 | result = type("Potion", (object,), result_dict)( | ||
| 135 | {}, max(self.duration, other.duration) | 136 | {}, max(self.duration, other.duration) | ||
| 136 | ) | 137 | ) | ||
| 137 | self.is_component = True | 138 | self.is_component = True | ||
| 138 | other.is_component = True | 139 | other.is_component = True | ||
| 139 | 140 | ||||
| 140 | return result | 141 | return result | ||
| 141 | 142 | ||||
| 142 | def __mul__(self, other): | 143 | def __mul__(self, other): | ||
| 143 | self.validate_state_on_operation() | 144 | self.validate_state_on_operation() | ||
| 144 | result_dict = {} | 145 | result_dict = {} | ||
| 145 | # transfering the methods from the object to the dict and multiply them with the number | 146 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 146 | for key in dir(self): | 147 | for key in dir(self): | ||
| 147 | value = getattr(self, key) | 148 | value = getattr(self, key) | ||
| 148 | if type(value) is PotionEffect and not value.used: | 149 | if type(value) is PotionEffect and not value.used: | ||
| 149 | result_dict[key] = value.copy() | 150 | result_dict[key] = value.copy() | ||
| 150 | result_dict[key] *= other | 151 | result_dict[key] *= other | ||
| 151 | 152 | ||||
| 152 | Potion.update_dict_with_class_funcs(result_dict) | 153 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 153 | 154 | ||||
| 154 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 155 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 155 | self.is_component = True | 156 | self.is_component = True | ||
| 156 | 157 | ||||
| 157 | return result | 158 | return result | ||
| 158 | 159 | ||||
| 159 | def __sub__(self, other): | 160 | def __sub__(self, other): | ||
| 160 | self.validate_state_on_operation() | 161 | self.validate_state_on_operation() | ||
| 161 | other.validate_state_on_operation() | 162 | other.validate_state_on_operation() | ||
| 162 | result_dict = {} | 163 | result_dict = {} | ||
| 163 | # validation that the substitution is valid | 164 | # validation that the substitution is valid | ||
| 164 | for key in dir(other): | 165 | for key in dir(other): | ||
| 165 | value = getattr(other, key) | 166 | value = getattr(other, key) | ||
| 166 | if type(value) is PotionEffect and not value.used and not key in dir(self): | 167 | if type(value) is PotionEffect and not value.used and not key in dir(self): | ||
| 167 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 168 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 168 | # transfering the methods from the object on the left side to dict, | 169 | # transfering the methods from the object on the left side to dict, | ||
| 169 | # and if there are matching methods in the right side, we subtract their intensity | 170 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 170 | for key in dir(self): | 171 | for key in dir(self): | ||
| 171 | value = getattr(self, key) | 172 | value = getattr(self, key) | ||
| 172 | if not type(value) is PotionEffect or value.used: | 173 | if not type(value) is PotionEffect or value.used: | ||
| 173 | continue | 174 | continue | ||
| 174 | result_dict[key] = value.copy() | 175 | result_dict[key] = value.copy() | ||
| 175 | if key in dir(other): | 176 | if key in dir(other): | ||
| 176 | other_value = getattr(other, key) | 177 | other_value = getattr(other, key) | ||
| 177 | if not other_value.used: | 178 | if not other_value.used: | ||
| 178 | result_dict[key] -= getattr(other, key) | 179 | result_dict[key] -= getattr(other, key) | ||
| t | 179 | if result_dict[key].times < 0: | t | 180 | if result_dict[key].times <= 0: |
| 180 | result_dict.pop(key) | 181 | result_dict.pop(key) | ||
| 181 | 182 | ||||
| 182 | Potion.update_dict_with_class_funcs(result_dict) | 183 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 183 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 184 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 184 | self.is_component = True | 185 | self.is_component = True | ||
| 185 | other.is_component = True | 186 | other.is_component = True | ||
| 186 | return result | 187 | return result | ||
| 187 | 188 | ||||
| 188 | def __truediv__(self, other): | 189 | def __truediv__(self, other): | ||
| 189 | self.validate_state_on_operation() | 190 | self.validate_state_on_operation() | ||
| 190 | # spliting the object into equal parts, where the intensity is devided by the number | 191 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 191 | result_list = [] | 192 | result_list = [] | ||
| 192 | for _ in range(other): | 193 | for _ in range(other): | ||
| 193 | result_dict = {} | 194 | result_dict = {} | ||
| 194 | for key in dir(self): | 195 | for key in dir(self): | ||
| 195 | value = getattr(self, key) | 196 | value = getattr(self, key) | ||
| 196 | if type(value) is PotionEffect and not value.used: | 197 | if type(value) is PotionEffect and not value.used: | ||
| 197 | result_dict[key] = value.copy() | 198 | result_dict[key] = value.copy() | ||
| 198 | result_dict[key] /= other | 199 | result_dict[key] /= other | ||
| 199 | 200 | ||||
| 200 | Potion.update_dict_with_class_funcs(result_dict) | 201 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 201 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 202 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 202 | result_list.append(result) | 203 | result_list.append(result) | ||
| 203 | 204 | ||||
| 204 | self.is_component = True | 205 | self.is_component = True | ||
| 205 | return tuple(result_list) | 206 | return tuple(result_list) | ||
| 206 | 207 | ||||
| 207 | def __eq__(self, other): | 208 | def __eq__(self, other): | ||
| 208 | self.validate_state_on_operation() | 209 | self.validate_state_on_operation() | ||
| 209 | other.validate_state_on_operation() | 210 | other.validate_state_on_operation() | ||
| 210 | for key in dir(self): | 211 | for key in dir(self): | ||
| 211 | value = getattr(self, key) | 212 | value = getattr(self, key) | ||
| 212 | if type(value) is PotionEffect and not value.used: | 213 | if type(value) is PotionEffect and not value.used: | ||
| 213 | if not key in dir(other): | 214 | if not key in dir(other): | ||
| 214 | return False | 215 | return False | ||
| 215 | other_value = getattr(other, key) | 216 | other_value = getattr(other, key) | ||
| 216 | if value.times != other_value.times or value.used != other_value.used: | 217 | if value.times != other_value.times or value.used != other_value.used: | ||
| 217 | return False | 218 | return False | ||
| 218 | 219 | ||||
| 219 | for key in dir(other): | 220 | for key in dir(other): | ||
| 220 | value = getattr(other, key) | 221 | value = getattr(other, key) | ||
| 221 | if type(value) is PotionEffect and not value.used and not key in dir(self): | 222 | if type(value) is PotionEffect and not value.used and not key in dir(self): | ||
| 222 | return False | 223 | return False | ||
| 223 | 224 | ||||
| 224 | return True | 225 | return True | ||
| 225 | 226 | ||||
| 226 | def get_sum(self): | 227 | def get_sum(self): | ||
| 227 | result = 0 | 228 | result = 0 | ||
| 228 | for key in dir(self): | 229 | for key in dir(self): | ||
| 229 | value = getattr(self, key) | 230 | value = getattr(self, key) | ||
| 230 | if type(value) is PotionEffect and not value.used: | 231 | if type(value) is PotionEffect and not value.used: | ||
| 231 | result += value.times | 232 | result += value.times | ||
| 232 | return result | 233 | return result | ||
| 233 | 234 | ||||
| 234 | def __gt__(self, other): | 235 | def __gt__(self, other): | ||
| 235 | self.validate_state_on_operation() | 236 | self.validate_state_on_operation() | ||
| 236 | other.validate_state_on_operation() | 237 | other.validate_state_on_operation() | ||
| 237 | return self.get_sum() > other.get_sum() | 238 | return self.get_sum() > other.get_sum() | ||
| 238 | 239 | ||||
| 239 | def __lt__(self, other): | 240 | def __lt__(self, other): | ||
| 240 | self.validate_state_on_operation() | 241 | self.validate_state_on_operation() | ||
| 241 | other.validate_state_on_operation() | 242 | other.validate_state_on_operation() | ||
| 242 | return self.get_sum() < other.get_sum() | 243 | return self.get_sum() < other.get_sum() | ||
| 243 | 244 | ||||
| 244 | 245 | ||||
| 245 | class ГоспожатаПоХимия: | 246 | class ГоспожатаПоХимия: | ||
| 246 | @staticmethod | 247 | @staticmethod | ||
| 247 | def calculate_molecular_mass(element): | 248 | def calculate_molecular_mass(element): | ||
| 248 | return sum([ord(i) for i in element]) | 249 | return sum([ord(i) for i in element]) | ||
| 249 | 250 | ||||
| 250 | def __init__(self): | 251 | def __init__(self): | ||
| 251 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 252 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 252 | self.timer = {} | 253 | self.timer = {} | ||
| 253 | 254 | ||||
| 254 | def apply_effect(self, target, effect): | 255 | def apply_effect(self, target, effect): | ||
| 255 | # creating a revrse function to reverse the changes after the effect expires | 256 | # creating a revrse function to reverse the changes after the effect expires | ||
| 256 | old_attributes = vars(target).copy() | 257 | old_attributes = vars(target).copy() | ||
| 257 | effect(target) | 258 | effect(target) | ||
| 258 | mid__attributes = vars(target).copy() | 259 | mid__attributes = vars(target).copy() | ||
| 259 | 260 | ||||
| 260 | def reverse_effect(): | 261 | def reverse_effect(): | ||
| 261 | new_attributes = vars(target).copy() | 262 | new_attributes = vars(target).copy() | ||
| 262 | for key, value in new_attributes.items(): | 263 | for key, value in new_attributes.items(): | ||
| 263 | if not key in mid__attributes.keys(): | 264 | if not key in mid__attributes.keys(): | ||
| 264 | continue | 265 | continue | ||
| 265 | is_old = key in old_attributes.keys() | 266 | is_old = key in old_attributes.keys() | ||
| 266 | if type(value) in (int, float): | 267 | if type(value) in (int, float): | ||
| 267 | old_val = 0 | 268 | old_val = 0 | ||
| 268 | if is_old: | 269 | if is_old: | ||
| 269 | old_val = old_attributes[key] | 270 | old_val = old_attributes[key] | ||
| 270 | 271 | ||||
| 271 | if ( | 272 | if ( | ||
| 272 | value - (mid__attributes[key] - old_val) | 273 | value - (mid__attributes[key] - old_val) | ||
| 273 | ) == old_val and not is_old: | 274 | ) == old_val and not is_old: | ||
| 274 | delattr(target, key) | 275 | delattr(target, key) | ||
| 275 | else: | 276 | else: | ||
| 276 | setattr(target, key, value - (mid__attributes[key] - old_val)) | 277 | setattr(target, key, value - (mid__attributes[key] - old_val)) | ||
| 277 | elif value == mid__attributes[key] and key in old_attributes.keys(): | 278 | elif value == mid__attributes[key] and key in old_attributes.keys(): | ||
| 278 | setattr(target, key, old_attributes[key]) | 279 | setattr(target, key, old_attributes[key]) | ||
| 279 | elif not is_old: | 280 | elif not is_old: | ||
| 280 | delattr(target, key) | 281 | delattr(target, key) | ||
| 281 | 282 | ||||
| 282 | return reverse_effect | 283 | return reverse_effect | ||
| 283 | 284 | ||||
| 284 | def apply(self, target, potion): | 285 | def apply(self, target, potion): | ||
| 285 | if potion.is_used(): | 286 | if potion.is_used(): | ||
| 286 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 287 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 287 | 288 | ||||
| 288 | if potion.is_component: # this is probably useless, but just to be sure | 289 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 289 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 290 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 290 | 291 | ||||
| 291 | if potion.duration == 0: | 292 | if potion.duration == 0: | ||
| 292 | return | 293 | return | ||
| 293 | ordered_dir = sorted( | 294 | ordered_dir = sorted( | ||
| 294 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | 295 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | ||
| 295 | ) | 296 | ) | ||
| 296 | for key in ordered_dir: | 297 | for key in ordered_dir: | ||
| 297 | value = getattr(potion, key) | 298 | value = getattr(potion, key) | ||
| 298 | if type(value) is PotionEffect and not value.used: | 299 | if type(value) is PotionEffect and not value.used: | ||
| 299 | reverse_func = self.apply_effect(target, value) | 300 | reverse_func = self.apply_effect(target, value) | ||
| 300 | self.timer[reverse_func] = potion.duration | 301 | self.timer[reverse_func] = potion.duration | ||
| 301 | 302 | ||||
| 302 | def tick(self): | 303 | def tick(self): | ||
| 303 | to_iterate = self.timer.copy().items() | 304 | to_iterate = self.timer.copy().items() | ||
| 304 | for key, value in to_iterate: | 305 | for key, value in to_iterate: | ||
| 305 | self.timer[key] -= 1 | 306 | self.timer[key] -= 1 | ||
| 306 | if value <= 1: | 307 | if value <= 1: | ||
| 307 | key() | 308 | key() | ||
| 308 | self.timer.pop(key) | 309 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | class PotionEffect: | 9 | class PotionEffect: | ||
| 10 | @staticmethod | 10 | @staticmethod | ||
| 11 | def custom_round(value): | 11 | def custom_round(value): | ||
| 12 | result = round(value) | 12 | result = round(value) | ||
| 13 | if cmath.isclose( | 13 | if cmath.isclose( | ||
| 14 | result - value, 0.5 | 14 | result - value, 0.5 | ||
| 15 | ): # the edge case where x.5 should be rounded down | 15 | ): # the edge case where x.5 should be rounded down | ||
| 16 | result -= 1 | 16 | result -= 1 | ||
| 17 | return result | 17 | return result | ||
| 18 | 18 | ||||
| 19 | def __init__(self, func): | 19 | def __init__(self, func): | ||
| 20 | self.func = func | 20 | self.func = func | ||
| 21 | self.times = 1 | 21 | self.times = 1 | ||
| 22 | self.used = False | 22 | self.used = False | ||
| 23 | 23 | ||||
| 24 | def __iadd__(self, other): | 24 | def __iadd__(self, other): | ||
| 25 | self.times += other.times | 25 | self.times += other.times | ||
| 26 | return self | 26 | return self | ||
| 27 | 27 | ||||
| 28 | def __isub__(self, other): | 28 | def __isub__(self, other): | ||
| 29 | self.times -= other.times | 29 | self.times -= other.times | ||
| 30 | return self | 30 | return self | ||
| 31 | 31 | ||||
| 32 | def __imul__(self, other): | 32 | def __imul__(self, other): | ||
| 33 | self.times = PotionEffect.custom_round(self.times * other) | 33 | self.times = PotionEffect.custom_round(self.times * other) | ||
| 34 | return self | 34 | return self | ||
| 35 | 35 | ||||
| 36 | def __itruediv__(self, other): | 36 | def __itruediv__(self, other): | ||
| 37 | self.times = PotionEffect.custom_round(self.times / other) | 37 | self.times = PotionEffect.custom_round(self.times / other) | ||
| 38 | return self | 38 | return self | ||
| 39 | 39 | ||||
| 40 | def copy(self): | 40 | def copy(self): | ||
| 41 | result = PotionEffect(self.func) | 41 | result = PotionEffect(self.func) | ||
| 42 | result.times = self.times | 42 | result.times = self.times | ||
| 43 | result.used = self.used | 43 | result.used = self.used | ||
| 44 | return result | 44 | return result | ||
| 45 | 45 | ||||
| 46 | def __call__(self, target): | 46 | def __call__(self, target): | ||
| 47 | if self.used: | 47 | if self.used: | ||
| 48 | raise TypeError(EFFECT_ERROR_TEXT) | 48 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 49 | for _ in range(self.times): | 49 | for _ in range(self.times): | ||
| 50 | self.func(target) | 50 | self.func(target) | ||
| 51 | self.used = True | 51 | self.used = True | ||
| 52 | 52 | ||||
| 53 | 53 | ||||
| 54 | class meta(type): | 54 | class meta(type): | ||
| 55 | def __new__(cls, name, bases, attr_dict): | 55 | def __new__(cls, name, bases, attr_dict): | ||
| 56 | return type.__new__(cls, name, bases, attr_dict) | 56 | return type.__new__(cls, name, bases, attr_dict) | ||
| 57 | 57 | ||||
| 58 | 58 | ||||
| 59 | class Potion(metaclass=meta): | 59 | class Potion(metaclass=meta): | ||
| 60 | @staticmethod | 60 | @staticmethod | ||
| 61 | def fake_func_for_component(target): | 61 | def fake_func_for_component(target): | ||
| 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 63 | 63 | ||||
| 64 | @staticmethod | 64 | @staticmethod | ||
| 65 | def update_dict_with_class_funcs(result_dict): | 65 | def update_dict_with_class_funcs(result_dict): | ||
| 66 | result_dict.update( | 66 | result_dict.update( | ||
| 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | ||
| 68 | ) | 68 | ) | ||
| 69 | result_dict["__init__"] = Potion.__init__ | 69 | result_dict["__init__"] = Potion.__init__ | ||
| 70 | result_dict["__eq__"] = Potion.__eq__ | 70 | result_dict["__eq__"] = Potion.__eq__ | ||
| 71 | result_dict["__lt__"] = Potion.__lt__ | 71 | result_dict["__lt__"] = Potion.__lt__ | ||
| 72 | result_dict["__gt__"] = Potion.__gt__ | 72 | result_dict["__gt__"] = Potion.__gt__ | ||
| 73 | result_dict.pop("__weakref__") | 73 | result_dict.pop("__weakref__") | ||
| 74 | return result_dict | 74 | return result_dict | ||
| 75 | 75 | ||||
| 76 | def validate_state_on_operation(self): | 76 | def validate_state_on_operation(self): | ||
| 77 | if self.is_component: | 77 | if self.is_component: | ||
| 78 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 78 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 79 | if self.is_used(): | 79 | if self.is_used(): | ||
| 80 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 80 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 81 | 81 | ||||
| 82 | def __init__(self, effects, duration): | 82 | def __init__(self, effects, duration): | ||
| 83 | for key, value in effects.items(): | 83 | for key, value in effects.items(): | ||
| 84 | setattr(self, key, PotionEffect(value)) | 84 | setattr(self, key, PotionEffect(value)) | ||
| 85 | self.duration = duration | 85 | self.duration = duration | ||
| 86 | self.is_component = False | 86 | self.is_component = False | ||
| 87 | 87 | ||||
| 88 | def is_used(self): | 88 | def is_used(self): | ||
| 89 | for key in dir(self): | 89 | for key in dir(self): | ||
| 90 | value = getattr(self, key) | 90 | value = getattr(self, key) | ||
| 91 | if type(value) is PotionEffect and not value.used: | 91 | if type(value) is PotionEffect and not value.used: | ||
| 92 | return False | 92 | return False | ||
| 93 | return True | 93 | return True | ||
| 94 | 94 | ||||
| 95 | def __getattribute__(self, name): | 95 | def __getattribute__(self, name): | ||
| 96 | result = object.__getattribute__(self, name) | 96 | result = object.__getattribute__(self, name) | ||
| 97 | if ( | 97 | if ( | ||
| 98 | object.__getattribute__(self, "is_component") | 98 | object.__getattribute__(self, "is_component") | ||
| 99 | and type(result) is PotionEffect | 99 | and type(result) is PotionEffect | ||
| 100 | ): | 100 | ): | ||
| 101 | fake_result = PotionEffect(Potion.fake_func_for_component) | 101 | fake_result = PotionEffect(Potion.fake_func_for_component) | ||
| 102 | fake_result.times = result.times | 102 | fake_result.times = result.times | ||
| 103 | fake_result.used = result.used | 103 | fake_result.used = result.used | ||
| 104 | return fake_result | 104 | return fake_result | ||
| 105 | 105 | ||||
| 106 | return result | 106 | return result | ||
| 107 | 107 | ||||
| 108 | def __add__(self, other): | 108 | def __add__(self, other): | ||
| 109 | self.validate_state_on_operation() | 109 | self.validate_state_on_operation() | ||
| 110 | other.validate_state_on_operation() | 110 | other.validate_state_on_operation() | ||
| 111 | # transfering the methods from the object on the left side to dict, | 111 | # transfering the methods from the object on the left side to dict, | ||
| 112 | # and if there are matching methods in the right side, we increace the intensity | 112 | # and if there are matching methods in the right side, we increace the intensity | ||
| 113 | result_dict = {} | 113 | result_dict = {} | ||
| 114 | for key in dir(self): | 114 | for key in dir(self): | ||
| 115 | value = getattr(self, key) | 115 | value = getattr(self, key) | ||
| 116 | if type(value) is PotionEffect and not value.used: | 116 | if type(value) is PotionEffect and not value.used: | ||
| 117 | result_dict[key] = value.copy() | 117 | result_dict[key] = value.copy() | ||
| 118 | if key in dir(other): | 118 | if key in dir(other): | ||
| 119 | other_value = getattr(other, key) | 119 | other_value = getattr(other, key) | ||
| 120 | if not other_value.used: | 120 | if not other_value.used: | ||
| 121 | result_dict[key] += other_value | 121 | result_dict[key] += other_value | ||
| 122 | 122 | ||||
| 123 | # transfering the methods that are only in the right side to the dict | 123 | # transfering the methods that are only in the right side to the dict | ||
| 124 | for key in dir(other): | 124 | for key in dir(other): | ||
| 125 | value = getattr(other, key) | 125 | value = getattr(other, key) | ||
| 126 | if ( | 126 | if ( | ||
| 127 | type(value) is PotionEffect | 127 | type(value) is PotionEffect | ||
| 128 | and not value.used | 128 | and not value.used | ||
| 129 | and not key in result_dict.keys() | 129 | and not key in result_dict.keys() | ||
| 130 | ): | 130 | ): | ||
| 131 | result_dict[key] = getattr(other, key).copy() | 131 | result_dict[key] = getattr(other, key).copy() | ||
| 132 | 132 | ||||
| 133 | Potion.update_dict_with_class_funcs(result_dict) | 133 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 134 | result = type("Potion", (object,), result_dict)( | 134 | result = type("Potion", (object,), result_dict)( | ||
| 135 | {}, max(self.duration, other.duration) | 135 | {}, max(self.duration, other.duration) | ||
| 136 | ) | 136 | ) | ||
| 137 | self.is_component = True | 137 | self.is_component = True | ||
| 138 | other.is_component = True | 138 | other.is_component = True | ||
| 139 | 139 | ||||
| 140 | return result | 140 | return result | ||
| 141 | 141 | ||||
| 142 | def __mul__(self, other): | 142 | def __mul__(self, other): | ||
| 143 | self.validate_state_on_operation() | 143 | self.validate_state_on_operation() | ||
| 144 | result_dict = {} | 144 | result_dict = {} | ||
| 145 | # transfering the methods from the object to the dict and multiply them with the number | 145 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 146 | for key in dir(self): | 146 | for key in dir(self): | ||
| 147 | value = getattr(self, key) | 147 | value = getattr(self, key) | ||
| 148 | if type(value) is PotionEffect and not value.used: | 148 | if type(value) is PotionEffect and not value.used: | ||
| 149 | result_dict[key] = value.copy() | 149 | result_dict[key] = value.copy() | ||
| 150 | result_dict[key] *= other | 150 | result_dict[key] *= other | ||
| 151 | 151 | ||||
| 152 | Potion.update_dict_with_class_funcs(result_dict) | 152 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 153 | 153 | ||||
| 154 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 154 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 155 | self.is_component = True | 155 | self.is_component = True | ||
| 156 | 156 | ||||
| 157 | return result | 157 | return result | ||
| 158 | 158 | ||||
| 159 | def __sub__(self, other): | 159 | def __sub__(self, other): | ||
| 160 | self.validate_state_on_operation() | 160 | self.validate_state_on_operation() | ||
| 161 | other.validate_state_on_operation() | 161 | other.validate_state_on_operation() | ||
| 162 | result_dict = {} | 162 | result_dict = {} | ||
| 163 | # validation that the substitution is valid | 163 | # validation that the substitution is valid | ||
| 164 | for key in dir(other): | 164 | for key in dir(other): | ||
| 165 | value = getattr(other, key) | 165 | value = getattr(other, key) | ||
| 166 | if type(value) is PotionEffect and not value.used and not key in dir(self): | 166 | if type(value) is PotionEffect and not value.used and not key in dir(self): | ||
| 167 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 167 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 168 | # transfering the methods from the object on the left side to dict, | 168 | # transfering the methods from the object on the left side to dict, | ||
| 169 | # and if there are matching methods in the right side, we subtract their intensity | 169 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 170 | for key in dir(self): | 170 | for key in dir(self): | ||
| 171 | value = getattr(self, key) | 171 | value = getattr(self, key) | ||
| 172 | if not type(value) is PotionEffect or value.used: | 172 | if not type(value) is PotionEffect or value.used: | ||
| 173 | continue | 173 | continue | ||
| 174 | result_dict[key] = value.copy() | 174 | result_dict[key] = value.copy() | ||
| 175 | if key in dir(other): | 175 | if key in dir(other): | ||
| 176 | other_value = getattr(other, key) | 176 | other_value = getattr(other, key) | ||
| 177 | if not other_value.used: | 177 | if not other_value.used: | ||
| 178 | result_dict[key] -= getattr(other, key) | 178 | result_dict[key] -= getattr(other, key) | ||
| t | 179 | if result_dict[key].times <= 0: | t | 179 | if result_dict[key].times < 0: |
| 180 | result_dict.pop(key) | 180 | result_dict.pop(key) | ||
| 181 | 181 | ||||
| 182 | Potion.update_dict_with_class_funcs(result_dict) | 182 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 183 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 183 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 184 | self.is_component = True | 184 | self.is_component = True | ||
| 185 | other.is_component = True | 185 | other.is_component = True | ||
| 186 | return result | 186 | return result | ||
| 187 | 187 | ||||
| 188 | def __truediv__(self, other): | 188 | def __truediv__(self, other): | ||
| 189 | self.validate_state_on_operation() | 189 | self.validate_state_on_operation() | ||
| 190 | # spliting the object into equal parts, where the intensity is devided by the number | 190 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 191 | result_list = [] | 191 | result_list = [] | ||
| 192 | for _ in range(other): | 192 | for _ in range(other): | ||
| 193 | result_dict = {} | 193 | result_dict = {} | ||
| 194 | for key in dir(self): | 194 | for key in dir(self): | ||
| 195 | value = getattr(self, key) | 195 | value = getattr(self, key) | ||
| 196 | if type(value) is PotionEffect and not value.used: | 196 | if type(value) is PotionEffect and not value.used: | ||
| 197 | result_dict[key] = value.copy() | 197 | result_dict[key] = value.copy() | ||
| 198 | result_dict[key] /= other | 198 | result_dict[key] /= other | ||
| 199 | 199 | ||||
| 200 | Potion.update_dict_with_class_funcs(result_dict) | 200 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 201 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 201 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 202 | result_list.append(result) | 202 | result_list.append(result) | ||
| 203 | 203 | ||||
| 204 | self.is_component = True | 204 | self.is_component = True | ||
| 205 | return tuple(result_list) | 205 | return tuple(result_list) | ||
| 206 | 206 | ||||
| 207 | def __eq__(self, other): | 207 | def __eq__(self, other): | ||
| 208 | self.validate_state_on_operation() | 208 | self.validate_state_on_operation() | ||
| 209 | other.validate_state_on_operation() | 209 | other.validate_state_on_operation() | ||
| 210 | for key in dir(self): | 210 | for key in dir(self): | ||
| 211 | value = getattr(self, key) | 211 | value = getattr(self, key) | ||
| 212 | if type(value) is PotionEffect and not value.used: | 212 | if type(value) is PotionEffect and not value.used: | ||
| 213 | if not key in dir(other): | 213 | if not key in dir(other): | ||
| 214 | return False | 214 | return False | ||
| 215 | other_value = getattr(other, key) | 215 | other_value = getattr(other, key) | ||
| 216 | if value.times != other_value.times or value.used != other_value.used: | 216 | if value.times != other_value.times or value.used != other_value.used: | ||
| 217 | return False | 217 | return False | ||
| 218 | 218 | ||||
| 219 | for key in dir(other): | 219 | for key in dir(other): | ||
| 220 | value = getattr(other, key) | 220 | value = getattr(other, key) | ||
| 221 | if type(value) is PotionEffect and not value.used and not key in dir(self): | 221 | if type(value) is PotionEffect and not value.used and not key in dir(self): | ||
| 222 | return False | 222 | return False | ||
| 223 | 223 | ||||
| 224 | return True | 224 | return True | ||
| 225 | 225 | ||||
| 226 | def get_sum(self): | 226 | def get_sum(self): | ||
| 227 | result = 0 | 227 | result = 0 | ||
| 228 | for key in dir(self): | 228 | for key in dir(self): | ||
| 229 | value = getattr(self, key) | 229 | value = getattr(self, key) | ||
| 230 | if type(value) is PotionEffect and not value.used: | 230 | if type(value) is PotionEffect and not value.used: | ||
| 231 | result += value.times | 231 | result += value.times | ||
| 232 | return result | 232 | return result | ||
| 233 | 233 | ||||
| 234 | def __gt__(self, other): | 234 | def __gt__(self, other): | ||
| 235 | self.validate_state_on_operation() | 235 | self.validate_state_on_operation() | ||
| 236 | other.validate_state_on_operation() | 236 | other.validate_state_on_operation() | ||
| 237 | return self.get_sum() > other.get_sum() | 237 | return self.get_sum() > other.get_sum() | ||
| 238 | 238 | ||||
| 239 | def __lt__(self, other): | 239 | def __lt__(self, other): | ||
| 240 | self.validate_state_on_operation() | 240 | self.validate_state_on_operation() | ||
| 241 | other.validate_state_on_operation() | 241 | other.validate_state_on_operation() | ||
| 242 | return self.get_sum() < other.get_sum() | 242 | return self.get_sum() < other.get_sum() | ||
| 243 | 243 | ||||
| 244 | 244 | ||||
| 245 | class ГоспожатаПоХимия: | 245 | class ГоспожатаПоХимия: | ||
| 246 | @staticmethod | 246 | @staticmethod | ||
| 247 | def calculate_molecular_mass(element): | 247 | def calculate_molecular_mass(element): | ||
| 248 | return sum([ord(i) for i in element]) | 248 | return sum([ord(i) for i in element]) | ||
| 249 | 249 | ||||
| 250 | def __init__(self): | 250 | def __init__(self): | ||
| 251 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 251 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 252 | self.timer = {} | 252 | self.timer = {} | ||
| 253 | 253 | ||||
| 254 | def apply_effect(self, target, effect): | 254 | def apply_effect(self, target, effect): | ||
| 255 | # creating a revrse function to reverse the changes after the effect expires | 255 | # creating a revrse function to reverse the changes after the effect expires | ||
| 256 | old_attributes = vars(target).copy() | 256 | old_attributes = vars(target).copy() | ||
| 257 | effect(target) | 257 | effect(target) | ||
| 258 | mid__attributes = vars(target).copy() | 258 | mid__attributes = vars(target).copy() | ||
| 259 | 259 | ||||
| 260 | def reverse_effect(): | 260 | def reverse_effect(): | ||
| 261 | new_attributes = vars(target).copy() | 261 | new_attributes = vars(target).copy() | ||
| 262 | for key, value in new_attributes.items(): | 262 | for key, value in new_attributes.items(): | ||
| 263 | if not key in mid__attributes.keys(): | 263 | if not key in mid__attributes.keys(): | ||
| 264 | continue | 264 | continue | ||
| 265 | is_old = key in old_attributes.keys() | 265 | is_old = key in old_attributes.keys() | ||
| 266 | if type(value) in (int, float): | 266 | if type(value) in (int, float): | ||
| 267 | old_val = 0 | 267 | old_val = 0 | ||
| 268 | if is_old: | 268 | if is_old: | ||
| 269 | old_val = old_attributes[key] | 269 | old_val = old_attributes[key] | ||
| 270 | 270 | ||||
| 271 | if ( | 271 | if ( | ||
| 272 | value - (mid__attributes[key] - old_val) | 272 | value - (mid__attributes[key] - old_val) | ||
| 273 | ) == old_val and not is_old: | 273 | ) == old_val and not is_old: | ||
| 274 | delattr(target, key) | 274 | delattr(target, key) | ||
| 275 | else: | 275 | else: | ||
| 276 | setattr(target, key, value - (mid__attributes[key] - old_val)) | 276 | setattr(target, key, value - (mid__attributes[key] - old_val)) | ||
| 277 | elif value == mid__attributes[key] and key in old_attributes.keys(): | 277 | elif value == mid__attributes[key] and key in old_attributes.keys(): | ||
| 278 | setattr(target, key, old_attributes[key]) | 278 | setattr(target, key, old_attributes[key]) | ||
| 279 | elif not is_old: | 279 | elif not is_old: | ||
| 280 | delattr(target, key) | 280 | delattr(target, key) | ||
| 281 | 281 | ||||
| 282 | return reverse_effect | 282 | return reverse_effect | ||
| 283 | 283 | ||||
| 284 | def apply(self, target, potion): | 284 | def apply(self, target, potion): | ||
| 285 | if potion.is_used(): | 285 | if potion.is_used(): | ||
| 286 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 286 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 287 | 287 | ||||
| 288 | if potion.is_component: # this is probably useless, but just to be sure | 288 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 289 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 289 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 290 | 290 | ||||
| 291 | if potion.duration == 0: | 291 | if potion.duration == 0: | ||
| 292 | return | 292 | return | ||
| 293 | ordered_dir = sorted( | 293 | ordered_dir = sorted( | ||
| 294 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | 294 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | ||
| 295 | ) | 295 | ) | ||
| 296 | for key in ordered_dir: | 296 | for key in ordered_dir: | ||
| 297 | value = getattr(potion, key) | 297 | value = getattr(potion, key) | ||
| 298 | if type(value) is PotionEffect and not value.used: | 298 | if type(value) is PotionEffect and not value.used: | ||
| 299 | reverse_func = self.apply_effect(target, value) | 299 | reverse_func = self.apply_effect(target, value) | ||
| 300 | self.timer[reverse_func] = potion.duration | 300 | self.timer[reverse_func] = potion.duration | ||
| 301 | 301 | ||||
| 302 | def tick(self): | 302 | def tick(self): | ||
| 303 | to_iterate = self.timer.copy().items() | 303 | to_iterate = self.timer.copy().items() | ||
| 304 | for key, value in to_iterate: | 304 | for key, value in to_iterate: | ||
| 305 | self.timer[key] -= 1 | 305 | self.timer[key] -= 1 | ||
| 306 | if value <= 1: | 306 | if value <= 1: | ||
| 307 | key() | 307 | key() | ||
| 308 | self.timer.pop(key) | 308 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | class PotionEffect: | 9 | class PotionEffect: | ||
| 10 | @staticmethod | 10 | @staticmethod | ||
| 11 | def custom_round(value): | 11 | def custom_round(value): | ||
| 12 | result = round(value) | 12 | result = round(value) | ||
| 13 | if cmath.isclose( | 13 | if cmath.isclose( | ||
| 14 | result - value, 0.5 | 14 | result - value, 0.5 | ||
| 15 | ): # the edge case where x.5 should be rounded down | 15 | ): # the edge case where x.5 should be rounded down | ||
| 16 | result -= 1 | 16 | result -= 1 | ||
| 17 | return result | 17 | return result | ||
| 18 | 18 | ||||
| 19 | def __init__(self, func): | 19 | def __init__(self, func): | ||
| 20 | self.func = func | 20 | self.func = func | ||
| 21 | self.times = 1 | 21 | self.times = 1 | ||
| 22 | self.used = False | 22 | self.used = False | ||
| 23 | 23 | ||||
| 24 | def __iadd__(self, other): | 24 | def __iadd__(self, other): | ||
| 25 | self.times += other.times | 25 | self.times += other.times | ||
| 26 | return self | 26 | return self | ||
| 27 | 27 | ||||
| 28 | def __isub__(self, other): | 28 | def __isub__(self, other): | ||
| 29 | self.times -= other.times | 29 | self.times -= other.times | ||
| 30 | return self | 30 | return self | ||
| 31 | 31 | ||||
| 32 | def __imul__(self, other): | 32 | def __imul__(self, other): | ||
| 33 | self.times = PotionEffect.custom_round(self.times * other) | 33 | self.times = PotionEffect.custom_round(self.times * other) | ||
| 34 | return self | 34 | return self | ||
| 35 | 35 | ||||
| 36 | def __itruediv__(self, other): | 36 | def __itruediv__(self, other): | ||
| 37 | self.times = PotionEffect.custom_round(self.times / other) | 37 | self.times = PotionEffect.custom_round(self.times / other) | ||
| 38 | return self | 38 | return self | ||
| 39 | 39 | ||||
| 40 | def copy(self): | 40 | def copy(self): | ||
| 41 | result = PotionEffect(self.func) | 41 | result = PotionEffect(self.func) | ||
| 42 | result.times = self.times | 42 | result.times = self.times | ||
| 43 | result.used = self.used | 43 | result.used = self.used | ||
| 44 | return result | 44 | return result | ||
| 45 | 45 | ||||
| 46 | def __call__(self, target): | 46 | def __call__(self, target): | ||
| 47 | if self.used: | 47 | if self.used: | ||
| 48 | raise TypeError(EFFECT_ERROR_TEXT) | 48 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 49 | for _ in range(self.times): | 49 | for _ in range(self.times): | ||
| 50 | self.func(target) | 50 | self.func(target) | ||
| 51 | self.used = True | 51 | self.used = True | ||
| 52 | 52 | ||||
| 53 | 53 | ||||
| 54 | class meta(type): | 54 | class meta(type): | ||
| 55 | def __new__(cls, name, bases, attr_dict): | 55 | def __new__(cls, name, bases, attr_dict): | ||
| 56 | return type.__new__(cls, name, bases, attr_dict) | 56 | return type.__new__(cls, name, bases, attr_dict) | ||
| 57 | 57 | ||||
| 58 | 58 | ||||
| 59 | class Potion(metaclass=meta): | 59 | class Potion(metaclass=meta): | ||
| 60 | @staticmethod | 60 | @staticmethod | ||
| n | 61 | def fake_func(target): | n | 61 | def fake_func_for_component(target): |
| 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 63 | 63 | ||||
| 64 | @staticmethod | 64 | @staticmethod | ||
| 65 | def update_dict_with_class_funcs(result_dict): | 65 | def update_dict_with_class_funcs(result_dict): | ||
| 66 | result_dict.update( | 66 | result_dict.update( | ||
| 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | ||
| 68 | ) | 68 | ) | ||
| 69 | result_dict["__init__"] = Potion.__init__ | 69 | result_dict["__init__"] = Potion.__init__ | ||
| n | n | 70 | result_dict["__eq__"] = Potion.__eq__ | ||
| 71 | result_dict["__lt__"] = Potion.__lt__ | ||||
| 72 | result_dict["__gt__"] = Potion.__gt__ | ||||
| 70 | result_dict.pop("__weakref__") | 73 | result_dict.pop("__weakref__") | ||
| 71 | return result_dict | 74 | return result_dict | ||
| 72 | 75 | ||||
| 73 | def validate_state_on_operation(self): | 76 | def validate_state_on_operation(self): | ||
| 74 | if self.is_component: | 77 | if self.is_component: | ||
| 75 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 78 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 76 | if self.is_used(): | 79 | if self.is_used(): | ||
| 77 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 80 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 78 | 81 | ||||
| 79 | def __init__(self, effects, duration): | 82 | def __init__(self, effects, duration): | ||
| 80 | for key, value in effects.items(): | 83 | for key, value in effects.items(): | ||
| 81 | setattr(self, key, PotionEffect(value)) | 84 | setattr(self, key, PotionEffect(value)) | ||
| 82 | self.duration = duration | 85 | self.duration = duration | ||
| 83 | self.is_component = False | 86 | self.is_component = False | ||
| 84 | 87 | ||||
| 85 | def is_used(self): | 88 | def is_used(self): | ||
| 86 | for key in dir(self): | 89 | for key in dir(self): | ||
| 87 | value = getattr(self, key) | 90 | value = getattr(self, key) | ||
| 88 | if type(value) is PotionEffect and not value.used: | 91 | if type(value) is PotionEffect and not value.used: | ||
| 89 | return False | 92 | return False | ||
| 90 | return True | 93 | return True | ||
| 91 | 94 | ||||
| 92 | def __getattribute__(self, name): | 95 | def __getattribute__(self, name): | ||
| 93 | result = object.__getattribute__(self, name) | 96 | result = object.__getattribute__(self, name) | ||
| 94 | if ( | 97 | if ( | ||
| 95 | object.__getattribute__(self, "is_component") | 98 | object.__getattribute__(self, "is_component") | ||
| 96 | and type(result) is PotionEffect | 99 | and type(result) is PotionEffect | ||
| 97 | ): | 100 | ): | ||
| n | 98 | fake_result = PotionEffect(Potion.fake_func) | n | 101 | fake_result = PotionEffect(Potion.fake_func_for_component) |
| 99 | fake_result.times = result.times | 102 | fake_result.times = result.times | ||
| 100 | fake_result.used = result.used | 103 | fake_result.used = result.used | ||
| 101 | return fake_result | 104 | return fake_result | ||
| 102 | 105 | ||||
| 103 | return result | 106 | return result | ||
| 104 | 107 | ||||
| 105 | def __add__(self, other): | 108 | def __add__(self, other): | ||
| 106 | self.validate_state_on_operation() | 109 | self.validate_state_on_operation() | ||
| 107 | other.validate_state_on_operation() | 110 | other.validate_state_on_operation() | ||
| 108 | # transfering the methods from the object on the left side to dict, | 111 | # transfering the methods from the object on the left side to dict, | ||
| 109 | # and if there are matching methods in the right side, we increace the intensity | 112 | # and if there are matching methods in the right side, we increace the intensity | ||
| 110 | result_dict = {} | 113 | result_dict = {} | ||
| 111 | for key in dir(self): | 114 | for key in dir(self): | ||
| 112 | value = getattr(self, key) | 115 | value = getattr(self, key) | ||
| 113 | if type(value) is PotionEffect and not value.used: | 116 | if type(value) is PotionEffect and not value.used: | ||
| 114 | result_dict[key] = value.copy() | 117 | result_dict[key] = value.copy() | ||
| 115 | if key in dir(other): | 118 | if key in dir(other): | ||
| 116 | other_value = getattr(other, key) | 119 | other_value = getattr(other, key) | ||
| 117 | if not other_value.used: | 120 | if not other_value.used: | ||
| 118 | result_dict[key] += other_value | 121 | result_dict[key] += other_value | ||
| 119 | 122 | ||||
| 120 | # transfering the methods that are only in the right side to the dict | 123 | # transfering the methods that are only in the right side to the dict | ||
| 121 | for key in dir(other): | 124 | for key in dir(other): | ||
| 122 | value = getattr(other, key) | 125 | value = getattr(other, key) | ||
| n | 123 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | n | 126 | if ( |
| 127 | type(value) is PotionEffect | ||||
| 128 | and not value.used | ||||
| 129 | and not key in result_dict.keys() | ||||
| 130 | ): | ||||
| 124 | result_dict[key] = getattr(other, key).copy() | 131 | result_dict[key] = getattr(other, key).copy() | ||
| 125 | 132 | ||||
| 126 | Potion.update_dict_with_class_funcs(result_dict) | 133 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 127 | result = type("Potion", (object,), result_dict)( | 134 | result = type("Potion", (object,), result_dict)( | ||
| 128 | {}, max(self.duration, other.duration) | 135 | {}, max(self.duration, other.duration) | ||
| 129 | ) | 136 | ) | ||
| 130 | self.is_component = True | 137 | self.is_component = True | ||
| 131 | other.is_component = True | 138 | other.is_component = True | ||
| 132 | 139 | ||||
| 133 | return result | 140 | return result | ||
| 134 | 141 | ||||
| 135 | def __mul__(self, other): | 142 | def __mul__(self, other): | ||
| 136 | self.validate_state_on_operation() | 143 | self.validate_state_on_operation() | ||
| 137 | result_dict = {} | 144 | result_dict = {} | ||
| 138 | # transfering the methods from the object to the dict and multiply them with the number | 145 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 139 | for key in dir(self): | 146 | for key in dir(self): | ||
| 140 | value = getattr(self, key) | 147 | value = getattr(self, key) | ||
| n | 141 | if type(value) is PotionEffect: | n | 148 | if type(value) is PotionEffect and not value.used: |
| 142 | result_dict[key] = value.copy() | 149 | result_dict[key] = value.copy() | ||
| 143 | result_dict[key] *= other | 150 | result_dict[key] *= other | ||
| 144 | 151 | ||||
| 145 | Potion.update_dict_with_class_funcs(result_dict) | 152 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 146 | 153 | ||||
| 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 154 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 148 | self.is_component = True | 155 | self.is_component = True | ||
| 149 | 156 | ||||
| 150 | return result | 157 | return result | ||
| 151 | 158 | ||||
| 152 | def __sub__(self, other): | 159 | def __sub__(self, other): | ||
| 153 | self.validate_state_on_operation() | 160 | self.validate_state_on_operation() | ||
| 154 | other.validate_state_on_operation() | 161 | other.validate_state_on_operation() | ||
| 155 | result_dict = {} | 162 | result_dict = {} | ||
| 156 | # validation that the substitution is valid | 163 | # validation that the substitution is valid | ||
| 157 | for key in dir(other): | 164 | for key in dir(other): | ||
| 158 | value = getattr(other, key) | 165 | value = getattr(other, key) | ||
| n | 159 | if type(value) is PotionEffect and not key in dir(self): | n | 166 | if type(value) is PotionEffect and not value.used and not key in dir(self): |
| 160 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 167 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 161 | # transfering the methods from the object on the left side to dict, | 168 | # transfering the methods from the object on the left side to dict, | ||
| 162 | # and if there are matching methods in the right side, we subtract their intensity | 169 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 163 | for key in dir(self): | 170 | for key in dir(self): | ||
| 164 | value = getattr(self, key) | 171 | value = getattr(self, key) | ||
| n | 165 | if not type(value) is PotionEffect: | n | 172 | if not type(value) is PotionEffect or value.used: |
| 166 | continue | 173 | continue | ||
| 167 | result_dict[key] = value.copy() | 174 | result_dict[key] = value.copy() | ||
| 168 | if key in dir(other): | 175 | if key in dir(other): | ||
| 169 | other_value = getattr(other, key) | 176 | other_value = getattr(other, key) | ||
| 170 | if not other_value.used: | 177 | if not other_value.used: | ||
| 171 | result_dict[key] -= getattr(other, key) | 178 | result_dict[key] -= getattr(other, key) | ||
| 172 | if result_dict[key].times <= 0: | 179 | if result_dict[key].times <= 0: | ||
| 173 | result_dict.pop(key) | 180 | result_dict.pop(key) | ||
| 174 | 181 | ||||
| 175 | Potion.update_dict_with_class_funcs(result_dict) | 182 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 176 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 183 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 177 | self.is_component = True | 184 | self.is_component = True | ||
| 178 | other.is_component = True | 185 | other.is_component = True | ||
| 179 | return result | 186 | return result | ||
| 180 | 187 | ||||
| 181 | def __truediv__(self, other): | 188 | def __truediv__(self, other): | ||
| 182 | self.validate_state_on_operation() | 189 | self.validate_state_on_operation() | ||
| 183 | # spliting the object into equal parts, where the intensity is devided by the number | 190 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 184 | result_list = [] | 191 | result_list = [] | ||
| 185 | for _ in range(other): | 192 | for _ in range(other): | ||
| 186 | result_dict = {} | 193 | result_dict = {} | ||
| 187 | for key in dir(self): | 194 | for key in dir(self): | ||
| 188 | value = getattr(self, key) | 195 | value = getattr(self, key) | ||
| n | 189 | if type(value) is PotionEffect: | n | 196 | if type(value) is PotionEffect and not value.used: |
| 190 | result_dict[key] = value.copy() | 197 | result_dict[key] = value.copy() | ||
| 191 | result_dict[key] /= other | 198 | result_dict[key] /= other | ||
| 192 | 199 | ||||
| 193 | Potion.update_dict_with_class_funcs(result_dict) | 200 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 194 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 201 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 195 | result_list.append(result) | 202 | result_list.append(result) | ||
| 196 | 203 | ||||
| 197 | self.is_component = True | 204 | self.is_component = True | ||
| 198 | return tuple(result_list) | 205 | return tuple(result_list) | ||
| 199 | 206 | ||||
| 200 | def __eq__(self, other): | 207 | def __eq__(self, other): | ||
| 201 | self.validate_state_on_operation() | 208 | self.validate_state_on_operation() | ||
| 202 | other.validate_state_on_operation() | 209 | other.validate_state_on_operation() | ||
| n | 203 | n | |||
| 204 | for key in dir(self): | 210 | for key in dir(self): | ||
| 205 | value = getattr(self, key) | 211 | value = getattr(self, key) | ||
| 206 | if type(value) is PotionEffect and not value.used: | 212 | if type(value) is PotionEffect and not value.used: | ||
| 207 | if not key in dir(other): | 213 | if not key in dir(other): | ||
| 208 | return False | 214 | return False | ||
| 209 | other_value = getattr(other, key) | 215 | other_value = getattr(other, key) | ||
| 210 | if value.times != other_value.times or value.used != other_value.used: | 216 | if value.times != other_value.times or value.used != other_value.used: | ||
| 211 | return False | 217 | return False | ||
| 212 | 218 | ||||
| 213 | for key in dir(other): | 219 | for key in dir(other): | ||
| 214 | value = getattr(other, key) | 220 | value = getattr(other, key) | ||
| 215 | if type(value) is PotionEffect and not value.used and not key in dir(self): | 221 | if type(value) is PotionEffect and not value.used and not key in dir(self): | ||
| 216 | return False | 222 | return False | ||
| 217 | 223 | ||||
| 218 | return True | 224 | return True | ||
| 219 | 225 | ||||
| 220 | def get_sum(self): | 226 | def get_sum(self): | ||
| 221 | result = 0 | 227 | result = 0 | ||
| 222 | for key in dir(self): | 228 | for key in dir(self): | ||
| 223 | value = getattr(self, key) | 229 | value = getattr(self, key) | ||
| 224 | if type(value) is PotionEffect and not value.used: | 230 | if type(value) is PotionEffect and not value.used: | ||
| 225 | result += value.times | 231 | result += value.times | ||
| 226 | return result | 232 | return result | ||
| 227 | 233 | ||||
| 228 | def __gt__(self, other): | 234 | def __gt__(self, other): | ||
| 229 | self.validate_state_on_operation() | 235 | self.validate_state_on_operation() | ||
| 230 | other.validate_state_on_operation() | 236 | other.validate_state_on_operation() | ||
| 231 | return self.get_sum() > other.get_sum() | 237 | return self.get_sum() > other.get_sum() | ||
| 232 | 238 | ||||
| 233 | def __lt__(self, other): | 239 | def __lt__(self, other): | ||
| 234 | self.validate_state_on_operation() | 240 | self.validate_state_on_operation() | ||
| 235 | other.validate_state_on_operation() | 241 | other.validate_state_on_operation() | ||
| 236 | return self.get_sum() < other.get_sum() | 242 | return self.get_sum() < other.get_sum() | ||
| 237 | 243 | ||||
| 238 | 244 | ||||
| 239 | class ГоспожатаПоХимия: | 245 | class ГоспожатаПоХимия: | ||
| 240 | @staticmethod | 246 | @staticmethod | ||
| 241 | def calculate_molecular_mass(element): | 247 | def calculate_molecular_mass(element): | ||
| 242 | return sum([ord(i) for i in element]) | 248 | return sum([ord(i) for i in element]) | ||
| 243 | 249 | ||||
| 244 | def __init__(self): | 250 | def __init__(self): | ||
| 245 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 251 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 246 | self.timer = {} | 252 | self.timer = {} | ||
| 247 | 253 | ||||
| 248 | def apply_effect(self, target, effect): | 254 | def apply_effect(self, target, effect): | ||
| 249 | # creating a revrse function to reverse the changes after the effect expires | 255 | # creating a revrse function to reverse the changes after the effect expires | ||
| 250 | old_attributes = vars(target).copy() | 256 | old_attributes = vars(target).copy() | ||
| 251 | effect(target) | 257 | effect(target) | ||
| 252 | mid__attributes = vars(target).copy() | 258 | mid__attributes = vars(target).copy() | ||
| 253 | 259 | ||||
| 254 | def reverse_effect(): | 260 | def reverse_effect(): | ||
| 255 | new_attributes = vars(target).copy() | 261 | new_attributes = vars(target).copy() | ||
| 256 | for key, value in new_attributes.items(): | 262 | for key, value in new_attributes.items(): | ||
| 257 | if not key in mid__attributes.keys(): | 263 | if not key in mid__attributes.keys(): | ||
| 258 | continue | 264 | continue | ||
| n | n | 265 | is_old = key in old_attributes.keys() | ||
| 259 | if type(value) in (int, float): | 266 | if type(value) in (int, float): | ||
| 260 | old_val = 0 | 267 | old_val = 0 | ||
| n | 261 | if key in old_attributes.keys(): | n | 268 | if is_old: |
| 262 | old_val = old_attributes[key] | 269 | old_val = old_attributes[key] | ||
| 263 | 270 | ||||
| 264 | if ( | 271 | if ( | ||
| 265 | value - (mid__attributes[key] - old_val) | 272 | value - (mid__attributes[key] - old_val) | ||
| n | 266 | ) == old_val and old_val == 0: | n | 273 | ) == old_val and not is_old: |
| 267 | delattr(target, key) | 274 | delattr(target, key) | ||
| 268 | else: | 275 | else: | ||
| 269 | setattr(target, key, value - (mid__attributes[key] - old_val)) | 276 | setattr(target, key, value - (mid__attributes[key] - old_val)) | ||
| 270 | elif value == mid__attributes[key] and key in old_attributes.keys(): | 277 | elif value == mid__attributes[key] and key in old_attributes.keys(): | ||
| 271 | setattr(target, key, old_attributes[key]) | 278 | setattr(target, key, old_attributes[key]) | ||
| n | 272 | else: | n | 279 | elif not is_old: |
| 273 | delattr(target, key) | 280 | delattr(target, key) | ||
| 274 | 281 | ||||
| 275 | return reverse_effect | 282 | return reverse_effect | ||
| 276 | 283 | ||||
| 277 | def apply(self, target, potion): | 284 | def apply(self, target, potion): | ||
| 278 | if potion.is_used(): | 285 | if potion.is_used(): | ||
| 279 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 286 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 280 | 287 | ||||
| 281 | if potion.is_component: # this is probably useless, but just to be sure | 288 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 282 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 289 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| t | t | 290 | |||
| 291 | if potion.duration == 0: | ||||
| 292 | return | ||||
| 283 | ordered_dir = sorted( | 293 | ordered_dir = sorted( | ||
| 284 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | 294 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | ||
| 285 | ) | 295 | ) | ||
| 286 | for key in ordered_dir: | 296 | for key in ordered_dir: | ||
| 287 | value = getattr(potion, key) | 297 | value = getattr(potion, key) | ||
| 288 | if type(value) is PotionEffect and not value.used: | 298 | if type(value) is PotionEffect and not value.used: | ||
| 289 | reverse_func = self.apply_effect(target, value) | 299 | reverse_func = self.apply_effect(target, value) | ||
| 290 | self.timer[reverse_func] = potion.duration | 300 | self.timer[reverse_func] = potion.duration | ||
| 291 | 301 | ||||
| 292 | def tick(self): | 302 | def tick(self): | ||
| 293 | to_iterate = self.timer.copy().items() | 303 | to_iterate = self.timer.copy().items() | ||
| 294 | for key, value in to_iterate: | 304 | for key, value in to_iterate: | ||
| 295 | self.timer[key] -= 1 | 305 | self.timer[key] -= 1 | ||
| 296 | if value <= 1: | 306 | if value <= 1: | ||
| 297 | key() | 307 | key() | ||
| 298 | self.timer.pop(key) | 308 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | class PotionEffect: | 9 | class PotionEffect: | ||
| 10 | @staticmethod | 10 | @staticmethod | ||
| 11 | def custom_round(value): | 11 | def custom_round(value): | ||
| 12 | result = round(value) | 12 | result = round(value) | ||
| 13 | if cmath.isclose( | 13 | if cmath.isclose( | ||
| 14 | result - value, 0.5 | 14 | result - value, 0.5 | ||
| 15 | ): # the edge case where x.5 should be rounded down | 15 | ): # the edge case where x.5 should be rounded down | ||
| 16 | result -= 1 | 16 | result -= 1 | ||
| 17 | return result | 17 | return result | ||
| 18 | 18 | ||||
| 19 | def __init__(self, func): | 19 | def __init__(self, func): | ||
| 20 | self.func = func | 20 | self.func = func | ||
| 21 | self.times = 1 | 21 | self.times = 1 | ||
| 22 | self.used = False | 22 | self.used = False | ||
| 23 | 23 | ||||
| 24 | def __iadd__(self, other): | 24 | def __iadd__(self, other): | ||
| 25 | self.times += other.times | 25 | self.times += other.times | ||
| 26 | return self | 26 | return self | ||
| 27 | 27 | ||||
| 28 | def __isub__(self, other): | 28 | def __isub__(self, other): | ||
| 29 | self.times -= other.times | 29 | self.times -= other.times | ||
| 30 | return self | 30 | return self | ||
| 31 | 31 | ||||
| 32 | def __imul__(self, other): | 32 | def __imul__(self, other): | ||
| 33 | self.times = PotionEffect.custom_round(self.times * other) | 33 | self.times = PotionEffect.custom_round(self.times * other) | ||
| 34 | return self | 34 | return self | ||
| 35 | 35 | ||||
| 36 | def __itruediv__(self, other): | 36 | def __itruediv__(self, other): | ||
| 37 | self.times = PotionEffect.custom_round(self.times / other) | 37 | self.times = PotionEffect.custom_round(self.times / other) | ||
| 38 | return self | 38 | return self | ||
| 39 | 39 | ||||
| 40 | def copy(self): | 40 | def copy(self): | ||
| 41 | result = PotionEffect(self.func) | 41 | result = PotionEffect(self.func) | ||
| 42 | result.times = self.times | 42 | result.times = self.times | ||
| 43 | result.used = self.used | 43 | result.used = self.used | ||
| 44 | return result | 44 | return result | ||
| 45 | 45 | ||||
| 46 | def __call__(self, target): | 46 | def __call__(self, target): | ||
| 47 | if self.used: | 47 | if self.used: | ||
| 48 | raise TypeError(EFFECT_ERROR_TEXT) | 48 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 49 | for _ in range(self.times): | 49 | for _ in range(self.times): | ||
| 50 | self.func(target) | 50 | self.func(target) | ||
| 51 | self.used = True | 51 | self.used = True | ||
| 52 | 52 | ||||
| 53 | 53 | ||||
| 54 | class meta(type): | 54 | class meta(type): | ||
| 55 | def __new__(cls, name, bases, attr_dict): | 55 | def __new__(cls, name, bases, attr_dict): | ||
| 56 | return type.__new__(cls, name, bases, attr_dict) | 56 | return type.__new__(cls, name, bases, attr_dict) | ||
| 57 | 57 | ||||
| 58 | 58 | ||||
| 59 | class Potion(metaclass=meta): | 59 | class Potion(metaclass=meta): | ||
| 60 | @staticmethod | 60 | @staticmethod | ||
| 61 | def fake_func(target): | 61 | def fake_func(target): | ||
| 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 63 | 63 | ||||
| 64 | @staticmethod | 64 | @staticmethod | ||
| 65 | def update_dict_with_class_funcs(result_dict): | 65 | def update_dict_with_class_funcs(result_dict): | ||
| 66 | result_dict.update( | 66 | result_dict.update( | ||
| 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | ||
| 68 | ) | 68 | ) | ||
| 69 | result_dict["__init__"] = Potion.__init__ | 69 | result_dict["__init__"] = Potion.__init__ | ||
| 70 | result_dict.pop("__weakref__") | 70 | result_dict.pop("__weakref__") | ||
| 71 | return result_dict | 71 | return result_dict | ||
| 72 | 72 | ||||
| 73 | def validate_state_on_operation(self): | 73 | def validate_state_on_operation(self): | ||
| 74 | if self.is_component: | 74 | if self.is_component: | ||
| 75 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 75 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 76 | if self.is_used(): | 76 | if self.is_used(): | ||
| 77 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 77 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 78 | 78 | ||||
| 79 | def __init__(self, effects, duration): | 79 | def __init__(self, effects, duration): | ||
| 80 | for key, value in effects.items(): | 80 | for key, value in effects.items(): | ||
| 81 | setattr(self, key, PotionEffect(value)) | 81 | setattr(self, key, PotionEffect(value)) | ||
| 82 | self.duration = duration | 82 | self.duration = duration | ||
| 83 | self.is_component = False | 83 | self.is_component = False | ||
| 84 | 84 | ||||
| 85 | def is_used(self): | 85 | def is_used(self): | ||
| 86 | for key in dir(self): | 86 | for key in dir(self): | ||
| 87 | value = getattr(self, key) | 87 | value = getattr(self, key) | ||
| 88 | if type(value) is PotionEffect and not value.used: | 88 | if type(value) is PotionEffect and not value.used: | ||
| 89 | return False | 89 | return False | ||
| 90 | return True | 90 | return True | ||
| 91 | 91 | ||||
| 92 | def __getattribute__(self, name): | 92 | def __getattribute__(self, name): | ||
| 93 | result = object.__getattribute__(self, name) | 93 | result = object.__getattribute__(self, name) | ||
| 94 | if ( | 94 | if ( | ||
| 95 | object.__getattribute__(self, "is_component") | 95 | object.__getattribute__(self, "is_component") | ||
| 96 | and type(result) is PotionEffect | 96 | and type(result) is PotionEffect | ||
| 97 | ): | 97 | ): | ||
| 98 | fake_result = PotionEffect(Potion.fake_func) | 98 | fake_result = PotionEffect(Potion.fake_func) | ||
| 99 | fake_result.times = result.times | 99 | fake_result.times = result.times | ||
| 100 | fake_result.used = result.used | 100 | fake_result.used = result.used | ||
| 101 | return fake_result | 101 | return fake_result | ||
| 102 | 102 | ||||
| 103 | return result | 103 | return result | ||
| 104 | 104 | ||||
| 105 | def __add__(self, other): | 105 | def __add__(self, other): | ||
| 106 | self.validate_state_on_operation() | 106 | self.validate_state_on_operation() | ||
| 107 | other.validate_state_on_operation() | 107 | other.validate_state_on_operation() | ||
| 108 | # transfering the methods from the object on the left side to dict, | 108 | # transfering the methods from the object on the left side to dict, | ||
| 109 | # and if there are matching methods in the right side, we increace the intensity | 109 | # and if there are matching methods in the right side, we increace the intensity | ||
| 110 | result_dict = {} | 110 | result_dict = {} | ||
| 111 | for key in dir(self): | 111 | for key in dir(self): | ||
| 112 | value = getattr(self, key) | 112 | value = getattr(self, key) | ||
| 113 | if type(value) is PotionEffect and not value.used: | 113 | if type(value) is PotionEffect and not value.used: | ||
| 114 | result_dict[key] = value.copy() | 114 | result_dict[key] = value.copy() | ||
| 115 | if key in dir(other): | 115 | if key in dir(other): | ||
| 116 | other_value = getattr(other, key) | 116 | other_value = getattr(other, key) | ||
| 117 | if not other_value.used: | 117 | if not other_value.used: | ||
| 118 | result_dict[key] += other_value | 118 | result_dict[key] += other_value | ||
| 119 | 119 | ||||
| 120 | # transfering the methods that are only in the right side to the dict | 120 | # transfering the methods that are only in the right side to the dict | ||
| 121 | for key in dir(other): | 121 | for key in dir(other): | ||
| 122 | value = getattr(other, key) | 122 | value = getattr(other, key) | ||
| 123 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | 123 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | ||
| 124 | result_dict[key] = getattr(other, key).copy() | 124 | result_dict[key] = getattr(other, key).copy() | ||
| 125 | 125 | ||||
| 126 | Potion.update_dict_with_class_funcs(result_dict) | 126 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 127 | result = type("Potion", (object,), result_dict)( | 127 | result = type("Potion", (object,), result_dict)( | ||
| 128 | {}, max(self.duration, other.duration) | 128 | {}, max(self.duration, other.duration) | ||
| 129 | ) | 129 | ) | ||
| 130 | self.is_component = True | 130 | self.is_component = True | ||
| 131 | other.is_component = True | 131 | other.is_component = True | ||
| 132 | 132 | ||||
| 133 | return result | 133 | return result | ||
| 134 | 134 | ||||
| 135 | def __mul__(self, other): | 135 | def __mul__(self, other): | ||
| 136 | self.validate_state_on_operation() | 136 | self.validate_state_on_operation() | ||
| 137 | result_dict = {} | 137 | result_dict = {} | ||
| 138 | # transfering the methods from the object to the dict and multiply them with the number | 138 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 139 | for key in dir(self): | 139 | for key in dir(self): | ||
| 140 | value = getattr(self, key) | 140 | value = getattr(self, key) | ||
| 141 | if type(value) is PotionEffect: | 141 | if type(value) is PotionEffect: | ||
| 142 | result_dict[key] = value.copy() | 142 | result_dict[key] = value.copy() | ||
| 143 | result_dict[key] *= other | 143 | result_dict[key] *= other | ||
| 144 | 144 | ||||
| 145 | Potion.update_dict_with_class_funcs(result_dict) | 145 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 146 | 146 | ||||
| 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 148 | self.is_component = True | 148 | self.is_component = True | ||
| 149 | 149 | ||||
| 150 | return result | 150 | return result | ||
| 151 | 151 | ||||
| 152 | def __sub__(self, other): | 152 | def __sub__(self, other): | ||
| 153 | self.validate_state_on_operation() | 153 | self.validate_state_on_operation() | ||
| 154 | other.validate_state_on_operation() | 154 | other.validate_state_on_operation() | ||
| 155 | result_dict = {} | 155 | result_dict = {} | ||
| 156 | # validation that the substitution is valid | 156 | # validation that the substitution is valid | ||
| 157 | for key in dir(other): | 157 | for key in dir(other): | ||
| 158 | value = getattr(other, key) | 158 | value = getattr(other, key) | ||
| 159 | if type(value) is PotionEffect and not key in dir(self): | 159 | if type(value) is PotionEffect and not key in dir(self): | ||
| 160 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 160 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 161 | # transfering the methods from the object on the left side to dict, | 161 | # transfering the methods from the object on the left side to dict, | ||
| 162 | # and if there are matching methods in the right side, we subtract their intensity | 162 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 163 | for key in dir(self): | 163 | for key in dir(self): | ||
| 164 | value = getattr(self, key) | 164 | value = getattr(self, key) | ||
| 165 | if not type(value) is PotionEffect: | 165 | if not type(value) is PotionEffect: | ||
| 166 | continue | 166 | continue | ||
| 167 | result_dict[key] = value.copy() | 167 | result_dict[key] = value.copy() | ||
| 168 | if key in dir(other): | 168 | if key in dir(other): | ||
| 169 | other_value = getattr(other, key) | 169 | other_value = getattr(other, key) | ||
| 170 | if not other_value.used: | 170 | if not other_value.used: | ||
| 171 | result_dict[key] -= getattr(other, key) | 171 | result_dict[key] -= getattr(other, key) | ||
| 172 | if result_dict[key].times <= 0: | 172 | if result_dict[key].times <= 0: | ||
| 173 | result_dict.pop(key) | 173 | result_dict.pop(key) | ||
| 174 | 174 | ||||
| 175 | Potion.update_dict_with_class_funcs(result_dict) | 175 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 176 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 176 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 177 | self.is_component = True | 177 | self.is_component = True | ||
| 178 | other.is_component = True | 178 | other.is_component = True | ||
| 179 | return result | 179 | return result | ||
| 180 | 180 | ||||
| 181 | def __truediv__(self, other): | 181 | def __truediv__(self, other): | ||
| 182 | self.validate_state_on_operation() | 182 | self.validate_state_on_operation() | ||
| 183 | # spliting the object into equal parts, where the intensity is devided by the number | 183 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 184 | result_list = [] | 184 | result_list = [] | ||
| 185 | for _ in range(other): | 185 | for _ in range(other): | ||
| 186 | result_dict = {} | 186 | result_dict = {} | ||
| 187 | for key in dir(self): | 187 | for key in dir(self): | ||
| 188 | value = getattr(self, key) | 188 | value = getattr(self, key) | ||
| 189 | if type(value) is PotionEffect: | 189 | if type(value) is PotionEffect: | ||
| 190 | result_dict[key] = value.copy() | 190 | result_dict[key] = value.copy() | ||
| 191 | result_dict[key] /= other | 191 | result_dict[key] /= other | ||
| 192 | 192 | ||||
| 193 | Potion.update_dict_with_class_funcs(result_dict) | 193 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 194 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 194 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 195 | result_list.append(result) | 195 | result_list.append(result) | ||
| 196 | 196 | ||||
| 197 | self.is_component = True | 197 | self.is_component = True | ||
| 198 | return tuple(result_list) | 198 | return tuple(result_list) | ||
| 199 | 199 | ||||
| 200 | def __eq__(self, other): | 200 | def __eq__(self, other): | ||
| 201 | self.validate_state_on_operation() | 201 | self.validate_state_on_operation() | ||
| 202 | other.validate_state_on_operation() | 202 | other.validate_state_on_operation() | ||
| 203 | 203 | ||||
| 204 | for key in dir(self): | 204 | for key in dir(self): | ||
| 205 | value = getattr(self, key) | 205 | value = getattr(self, key) | ||
| n | 206 | if type(value) is PotionEffect: | n | 206 | if type(value) is PotionEffect and not value.used: |
| 207 | if not key in dir(other): | 207 | if not key in dir(other): | ||
| 208 | return False | 208 | return False | ||
| 209 | other_value = getattr(other, key) | 209 | other_value = getattr(other, key) | ||
| 210 | if value.times != other_value.times or value.used != other_value.used: | 210 | if value.times != other_value.times or value.used != other_value.used: | ||
| 211 | return False | 211 | return False | ||
| 212 | 212 | ||||
| 213 | for key in dir(other): | 213 | for key in dir(other): | ||
| 214 | value = getattr(other, key) | 214 | value = getattr(other, key) | ||
| n | 215 | if type(value) is PotionEffect and not key in dir(self): | n | 215 | if type(value) is PotionEffect and not value.used and not key in dir(self): |
| 216 | return False | 216 | return False | ||
| 217 | 217 | ||||
| 218 | return True | 218 | return True | ||
| 219 | 219 | ||||
| 220 | def get_sum(self): | 220 | def get_sum(self): | ||
| 221 | result = 0 | 221 | result = 0 | ||
| 222 | for key in dir(self): | 222 | for key in dir(self): | ||
| 223 | value = getattr(self, key) | 223 | value = getattr(self, key) | ||
| 224 | if type(value) is PotionEffect and not value.used: | 224 | if type(value) is PotionEffect and not value.used: | ||
| 225 | result += value.times | 225 | result += value.times | ||
| 226 | return result | 226 | return result | ||
| 227 | 227 | ||||
| 228 | def __gt__(self, other): | 228 | def __gt__(self, other): | ||
| 229 | self.validate_state_on_operation() | 229 | self.validate_state_on_operation() | ||
| 230 | other.validate_state_on_operation() | 230 | other.validate_state_on_operation() | ||
| 231 | return self.get_sum() > other.get_sum() | 231 | return self.get_sum() > other.get_sum() | ||
| 232 | 232 | ||||
| 233 | def __lt__(self, other): | 233 | def __lt__(self, other): | ||
| 234 | self.validate_state_on_operation() | 234 | self.validate_state_on_operation() | ||
| 235 | other.validate_state_on_operation() | 235 | other.validate_state_on_operation() | ||
| 236 | return self.get_sum() < other.get_sum() | 236 | return self.get_sum() < other.get_sum() | ||
| 237 | 237 | ||||
| 238 | 238 | ||||
| 239 | class ГоспожатаПоХимия: | 239 | class ГоспожатаПоХимия: | ||
| 240 | @staticmethod | 240 | @staticmethod | ||
| 241 | def calculate_molecular_mass(element): | 241 | def calculate_molecular_mass(element): | ||
| 242 | return sum([ord(i) for i in element]) | 242 | return sum([ord(i) for i in element]) | ||
| 243 | 243 | ||||
| 244 | def __init__(self): | 244 | def __init__(self): | ||
| 245 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 245 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 246 | self.timer = {} | 246 | self.timer = {} | ||
| 247 | 247 | ||||
| 248 | def apply_effect(self, target, effect): | 248 | def apply_effect(self, target, effect): | ||
| 249 | # creating a revrse function to reverse the changes after the effect expires | 249 | # creating a revrse function to reverse the changes after the effect expires | ||
| 250 | old_attributes = vars(target).copy() | 250 | old_attributes = vars(target).copy() | ||
| 251 | effect(target) | 251 | effect(target) | ||
| n | 252 | to_reverse = {} | n | 252 | mid__attributes = vars(target).copy() |
| 253 | # set to store the new attributes added by the effect | ||||
| 254 | new_attribuets = set() | ||||
| 255 | |||||
| 256 | for key, value in vars(target).items(): | ||||
| 257 | old_value = 0 | ||||
| 258 | if key in old_attributes.keys(): | ||||
| 259 | old_value = old_attributes[key] | ||||
| 260 | else: | ||||
| 261 | new_attribuets.add(key) | ||||
| 262 | if value != old_value: | ||||
| 263 | to_reverse[key] = value - old_value | ||||
| 264 | 253 | ||||
| 265 | def reverse_effect(): | 254 | def reverse_effect(): | ||
| n | n | 255 | new_attributes = vars(target).copy() | ||
| 266 | for key, value in to_reverse.items(): | 256 | for key, value in new_attributes.items(): | ||
| 257 | if not key in mid__attributes.keys(): | ||||
| 258 | continue | ||||
| 259 | if type(value) in (int, float): | ||||
| 260 | old_val = 0 | ||||
| 261 | if key in old_attributes.keys(): | ||||
| 262 | old_val = old_attributes[key] | ||||
| 263 | |||||
| 264 | if ( | ||||
| 265 | value - (mid__attributes[key] - old_val) | ||||
| 266 | ) == old_val and old_val == 0: | ||||
| 267 | new_value = getattr(target, key) | 267 | delattr(target, key) | ||
| 268 | if new_value - value == 0 and key in new_attribuets: | 268 | else: | ||
| 269 | # the attribute is added in the function and when it is depleted it should be deleted | 269 | setattr(target, key, value - (mid__attributes[key] - old_val)) | ||
| 270 | elif value == mid__attributes[key] and key in old_attributes.keys(): | ||||
| 271 | setattr(target, key, old_attributes[key]) | ||||
| 272 | else: | ||||
| 270 | delattr(target, key) | 273 | delattr(target, key) | ||
| t | 271 | else: | t | ||
| 272 | setattr(target, key, new_value - value) | ||||
| 273 | 274 | ||||
| 274 | return reverse_effect | 275 | return reverse_effect | ||
| 275 | 276 | ||||
| 276 | def apply(self, target, potion): | 277 | def apply(self, target, potion): | ||
| 277 | if potion.is_used(): | 278 | if potion.is_used(): | ||
| 278 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 279 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 279 | 280 | ||||
| 280 | if potion.is_component: # this is probably useless, but just to be sure | 281 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 281 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 282 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 282 | ordered_dir = sorted( | 283 | ordered_dir = sorted( | ||
| 283 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | 284 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | ||
| 284 | ) | 285 | ) | ||
| 285 | for key in ordered_dir: | 286 | for key in ordered_dir: | ||
| 286 | value = getattr(potion, key) | 287 | value = getattr(potion, key) | ||
| 287 | if type(value) is PotionEffect and not value.used: | 288 | if type(value) is PotionEffect and not value.used: | ||
| 288 | reverse_func = self.apply_effect(target, value) | 289 | reverse_func = self.apply_effect(target, value) | ||
| 289 | self.timer[reverse_func] = potion.duration | 290 | self.timer[reverse_func] = potion.duration | ||
| 290 | 291 | ||||
| 291 | def tick(self): | 292 | def tick(self): | ||
| 292 | to_iterate = self.timer.copy().items() | 293 | to_iterate = self.timer.copy().items() | ||
| 293 | for key, value in to_iterate: | 294 | for key, value in to_iterate: | ||
| 294 | self.timer[key] -= 1 | 295 | self.timer[key] -= 1 | ||
| 295 | if value <= 1: | 296 | if value <= 1: | ||
| 296 | key() | 297 | key() | ||
| 297 | self.timer.pop(key) | 298 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | class PotionEffect: | 9 | class PotionEffect: | ||
| 10 | @staticmethod | 10 | @staticmethod | ||
| 11 | def custom_round(value): | 11 | def custom_round(value): | ||
| 12 | result = round(value) | 12 | result = round(value) | ||
| 13 | if cmath.isclose( | 13 | if cmath.isclose( | ||
| 14 | result - value, 0.5 | 14 | result - value, 0.5 | ||
| 15 | ): # the edge case where x.5 should be rounded down | 15 | ): # the edge case where x.5 should be rounded down | ||
| 16 | result -= 1 | 16 | result -= 1 | ||
| 17 | return result | 17 | return result | ||
| 18 | 18 | ||||
| 19 | def __init__(self, func): | 19 | def __init__(self, func): | ||
| 20 | self.func = func | 20 | self.func = func | ||
| 21 | self.times = 1 | 21 | self.times = 1 | ||
| 22 | self.used = False | 22 | self.used = False | ||
| 23 | 23 | ||||
| 24 | def __iadd__(self, other): | 24 | def __iadd__(self, other): | ||
| 25 | self.times += other.times | 25 | self.times += other.times | ||
| 26 | return self | 26 | return self | ||
| 27 | 27 | ||||
| 28 | def __isub__(self, other): | 28 | def __isub__(self, other): | ||
| 29 | self.times -= other.times | 29 | self.times -= other.times | ||
| 30 | return self | 30 | return self | ||
| 31 | 31 | ||||
| 32 | def __imul__(self, other): | 32 | def __imul__(self, other): | ||
| 33 | self.times = PotionEffect.custom_round(self.times * other) | 33 | self.times = PotionEffect.custom_round(self.times * other) | ||
| 34 | return self | 34 | return self | ||
| 35 | 35 | ||||
| 36 | def __itruediv__(self, other): | 36 | def __itruediv__(self, other): | ||
| 37 | self.times = PotionEffect.custom_round(self.times / other) | 37 | self.times = PotionEffect.custom_round(self.times / other) | ||
| 38 | return self | 38 | return self | ||
| 39 | 39 | ||||
| 40 | def copy(self): | 40 | def copy(self): | ||
| 41 | result = PotionEffect(self.func) | 41 | result = PotionEffect(self.func) | ||
| 42 | result.times = self.times | 42 | result.times = self.times | ||
| 43 | result.used = self.used | 43 | result.used = self.used | ||
| 44 | return result | 44 | return result | ||
| 45 | 45 | ||||
| 46 | def __call__(self, target): | 46 | def __call__(self, target): | ||
| 47 | if self.used: | 47 | if self.used: | ||
| 48 | raise TypeError(EFFECT_ERROR_TEXT) | 48 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 49 | for _ in range(self.times): | 49 | for _ in range(self.times): | ||
| 50 | self.func(target) | 50 | self.func(target) | ||
| 51 | self.used = True | 51 | self.used = True | ||
| 52 | 52 | ||||
| 53 | 53 | ||||
| 54 | class meta(type): | 54 | class meta(type): | ||
| 55 | def __new__(cls, name, bases, attr_dict): | 55 | def __new__(cls, name, bases, attr_dict): | ||
| 56 | return type.__new__(cls, name, bases, attr_dict) | 56 | return type.__new__(cls, name, bases, attr_dict) | ||
| 57 | 57 | ||||
| 58 | 58 | ||||
| 59 | class Potion(metaclass=meta): | 59 | class Potion(metaclass=meta): | ||
| 60 | @staticmethod | 60 | @staticmethod | ||
| 61 | def fake_func(target): | 61 | def fake_func(target): | ||
| 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 63 | 63 | ||||
| 64 | @staticmethod | 64 | @staticmethod | ||
| 65 | def update_dict_with_class_funcs(result_dict): | 65 | def update_dict_with_class_funcs(result_dict): | ||
| 66 | result_dict.update( | 66 | result_dict.update( | ||
| 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | ||
| 68 | ) | 68 | ) | ||
| 69 | result_dict["__init__"] = Potion.__init__ | 69 | result_dict["__init__"] = Potion.__init__ | ||
| 70 | result_dict.pop("__weakref__") | 70 | result_dict.pop("__weakref__") | ||
| 71 | return result_dict | 71 | return result_dict | ||
| 72 | 72 | ||||
| 73 | def validate_state_on_operation(self): | 73 | def validate_state_on_operation(self): | ||
| 74 | if self.is_component: | 74 | if self.is_component: | ||
| 75 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 75 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 76 | if self.is_used(): | 76 | if self.is_used(): | ||
| 77 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 77 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 78 | 78 | ||||
| 79 | def __init__(self, effects, duration): | 79 | def __init__(self, effects, duration): | ||
| 80 | for key, value in effects.items(): | 80 | for key, value in effects.items(): | ||
| 81 | setattr(self, key, PotionEffect(value)) | 81 | setattr(self, key, PotionEffect(value)) | ||
| 82 | self.duration = duration | 82 | self.duration = duration | ||
| 83 | self.is_component = False | 83 | self.is_component = False | ||
| 84 | 84 | ||||
| 85 | def is_used(self): | 85 | def is_used(self): | ||
| 86 | for key in dir(self): | 86 | for key in dir(self): | ||
| 87 | value = getattr(self, key) | 87 | value = getattr(self, key) | ||
| 88 | if type(value) is PotionEffect and not value.used: | 88 | if type(value) is PotionEffect and not value.used: | ||
| 89 | return False | 89 | return False | ||
| 90 | return True | 90 | return True | ||
| 91 | 91 | ||||
| 92 | def __getattribute__(self, name): | 92 | def __getattribute__(self, name): | ||
| 93 | result = object.__getattribute__(self, name) | 93 | result = object.__getattribute__(self, name) | ||
| 94 | if ( | 94 | if ( | ||
| 95 | object.__getattribute__(self, "is_component") | 95 | object.__getattribute__(self, "is_component") | ||
| 96 | and type(result) is PotionEffect | 96 | and type(result) is PotionEffect | ||
| 97 | ): | 97 | ): | ||
| 98 | fake_result = PotionEffect(Potion.fake_func) | 98 | fake_result = PotionEffect(Potion.fake_func) | ||
| 99 | fake_result.times = result.times | 99 | fake_result.times = result.times | ||
| 100 | fake_result.used = result.used | 100 | fake_result.used = result.used | ||
| 101 | return fake_result | 101 | return fake_result | ||
| 102 | 102 | ||||
| 103 | return result | 103 | return result | ||
| 104 | 104 | ||||
| 105 | def __add__(self, other): | 105 | def __add__(self, other): | ||
| 106 | self.validate_state_on_operation() | 106 | self.validate_state_on_operation() | ||
| 107 | other.validate_state_on_operation() | 107 | other.validate_state_on_operation() | ||
| 108 | # transfering the methods from the object on the left side to dict, | 108 | # transfering the methods from the object on the left side to dict, | ||
| 109 | # and if there are matching methods in the right side, we increace the intensity | 109 | # and if there are matching methods in the right side, we increace the intensity | ||
| 110 | result_dict = {} | 110 | result_dict = {} | ||
| 111 | for key in dir(self): | 111 | for key in dir(self): | ||
| 112 | value = getattr(self, key) | 112 | value = getattr(self, key) | ||
| 113 | if type(value) is PotionEffect and not value.used: | 113 | if type(value) is PotionEffect and not value.used: | ||
| 114 | result_dict[key] = value.copy() | 114 | result_dict[key] = value.copy() | ||
| 115 | if key in dir(other): | 115 | if key in dir(other): | ||
| 116 | other_value = getattr(other, key) | 116 | other_value = getattr(other, key) | ||
| 117 | if not other_value.used: | 117 | if not other_value.used: | ||
| 118 | result_dict[key] += other_value | 118 | result_dict[key] += other_value | ||
| 119 | 119 | ||||
| 120 | # transfering the methods that are only in the right side to the dict | 120 | # transfering the methods that are only in the right side to the dict | ||
| 121 | for key in dir(other): | 121 | for key in dir(other): | ||
| 122 | value = getattr(other, key) | 122 | value = getattr(other, key) | ||
| 123 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | 123 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | ||
| 124 | result_dict[key] = getattr(other, key).copy() | 124 | result_dict[key] = getattr(other, key).copy() | ||
| 125 | 125 | ||||
| 126 | Potion.update_dict_with_class_funcs(result_dict) | 126 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 127 | result = type("Potion", (object,), result_dict)( | 127 | result = type("Potion", (object,), result_dict)( | ||
| 128 | {}, max(self.duration, other.duration) | 128 | {}, max(self.duration, other.duration) | ||
| 129 | ) | 129 | ) | ||
| 130 | self.is_component = True | 130 | self.is_component = True | ||
| 131 | other.is_component = True | 131 | other.is_component = True | ||
| 132 | 132 | ||||
| 133 | return result | 133 | return result | ||
| 134 | 134 | ||||
| 135 | def __mul__(self, other): | 135 | def __mul__(self, other): | ||
| 136 | self.validate_state_on_operation() | 136 | self.validate_state_on_operation() | ||
| 137 | result_dict = {} | 137 | result_dict = {} | ||
| 138 | # transfering the methods from the object to the dict and multiply them with the number | 138 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 139 | for key in dir(self): | 139 | for key in dir(self): | ||
| 140 | value = getattr(self, key) | 140 | value = getattr(self, key) | ||
| 141 | if type(value) is PotionEffect: | 141 | if type(value) is PotionEffect: | ||
| 142 | result_dict[key] = value.copy() | 142 | result_dict[key] = value.copy() | ||
| 143 | result_dict[key] *= other | 143 | result_dict[key] *= other | ||
| 144 | 144 | ||||
| 145 | Potion.update_dict_with_class_funcs(result_dict) | 145 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 146 | 146 | ||||
| 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 148 | self.is_component = True | 148 | self.is_component = True | ||
| 149 | 149 | ||||
| 150 | return result | 150 | return result | ||
| 151 | 151 | ||||
| 152 | def __sub__(self, other): | 152 | def __sub__(self, other): | ||
| 153 | self.validate_state_on_operation() | 153 | self.validate_state_on_operation() | ||
| 154 | other.validate_state_on_operation() | 154 | other.validate_state_on_operation() | ||
| 155 | result_dict = {} | 155 | result_dict = {} | ||
| 156 | # validation that the substitution is valid | 156 | # validation that the substitution is valid | ||
| 157 | for key in dir(other): | 157 | for key in dir(other): | ||
| 158 | value = getattr(other, key) | 158 | value = getattr(other, key) | ||
| 159 | if type(value) is PotionEffect and not key in dir(self): | 159 | if type(value) is PotionEffect and not key in dir(self): | ||
| 160 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 160 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 161 | # transfering the methods from the object on the left side to dict, | 161 | # transfering the methods from the object on the left side to dict, | ||
| 162 | # and if there are matching methods in the right side, we subtract their intensity | 162 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 163 | for key in dir(self): | 163 | for key in dir(self): | ||
| 164 | value = getattr(self, key) | 164 | value = getattr(self, key) | ||
| 165 | if not type(value) is PotionEffect: | 165 | if not type(value) is PotionEffect: | ||
| 166 | continue | 166 | continue | ||
| 167 | result_dict[key] = value.copy() | 167 | result_dict[key] = value.copy() | ||
| 168 | if key in dir(other): | 168 | if key in dir(other): | ||
| 169 | other_value = getattr(other, key) | 169 | other_value = getattr(other, key) | ||
| 170 | if not other_value.used: | 170 | if not other_value.used: | ||
| 171 | result_dict[key] -= getattr(other, key) | 171 | result_dict[key] -= getattr(other, key) | ||
| 172 | if result_dict[key].times <= 0: | 172 | if result_dict[key].times <= 0: | ||
| 173 | result_dict.pop(key) | 173 | result_dict.pop(key) | ||
| 174 | 174 | ||||
| 175 | Potion.update_dict_with_class_funcs(result_dict) | 175 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 176 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 176 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 177 | self.is_component = True | 177 | self.is_component = True | ||
| 178 | other.is_component = True | 178 | other.is_component = True | ||
| 179 | return result | 179 | return result | ||
| 180 | 180 | ||||
| 181 | def __truediv__(self, other): | 181 | def __truediv__(self, other): | ||
| 182 | self.validate_state_on_operation() | 182 | self.validate_state_on_operation() | ||
| 183 | # spliting the object into equal parts, where the intensity is devided by the number | 183 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 184 | result_list = [] | 184 | result_list = [] | ||
| 185 | for _ in range(other): | 185 | for _ in range(other): | ||
| 186 | result_dict = {} | 186 | result_dict = {} | ||
| 187 | for key in dir(self): | 187 | for key in dir(self): | ||
| 188 | value = getattr(self, key) | 188 | value = getattr(self, key) | ||
| 189 | if type(value) is PotionEffect: | 189 | if type(value) is PotionEffect: | ||
| 190 | result_dict[key] = value.copy() | 190 | result_dict[key] = value.copy() | ||
| 191 | result_dict[key] /= other | 191 | result_dict[key] /= other | ||
| 192 | 192 | ||||
| 193 | Potion.update_dict_with_class_funcs(result_dict) | 193 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 194 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 194 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 195 | result_list.append(result) | 195 | result_list.append(result) | ||
| 196 | 196 | ||||
| 197 | self.is_component = True | 197 | self.is_component = True | ||
| 198 | return tuple(result_list) | 198 | return tuple(result_list) | ||
| 199 | 199 | ||||
| 200 | def __eq__(self, other): | 200 | def __eq__(self, other): | ||
| 201 | self.validate_state_on_operation() | 201 | self.validate_state_on_operation() | ||
| 202 | other.validate_state_on_operation() | 202 | other.validate_state_on_operation() | ||
| 203 | 203 | ||||
| 204 | for key in dir(self): | 204 | for key in dir(self): | ||
| 205 | value = getattr(self, key) | 205 | value = getattr(self, key) | ||
| 206 | if type(value) is PotionEffect: | 206 | if type(value) is PotionEffect: | ||
| 207 | if not key in dir(other): | 207 | if not key in dir(other): | ||
| 208 | return False | 208 | return False | ||
| 209 | other_value = getattr(other, key) | 209 | other_value = getattr(other, key) | ||
| 210 | if value.times != other_value.times or value.used != other_value.used: | 210 | if value.times != other_value.times or value.used != other_value.used: | ||
| 211 | return False | 211 | return False | ||
| 212 | 212 | ||||
| 213 | for key in dir(other): | 213 | for key in dir(other): | ||
| 214 | value = getattr(other, key) | 214 | value = getattr(other, key) | ||
| 215 | if type(value) is PotionEffect and not key in dir(self): | 215 | if type(value) is PotionEffect and not key in dir(self): | ||
| 216 | return False | 216 | return False | ||
| 217 | 217 | ||||
| 218 | return True | 218 | return True | ||
| 219 | 219 | ||||
| 220 | def get_sum(self): | 220 | def get_sum(self): | ||
| 221 | result = 0 | 221 | result = 0 | ||
| 222 | for key in dir(self): | 222 | for key in dir(self): | ||
| 223 | value = getattr(self, key) | 223 | value = getattr(self, key) | ||
| t | 224 | if type(value) is PotionEffect: | t | 224 | if type(value) is PotionEffect and not value.used: |
| 225 | result += value.times | 225 | result += value.times | ||
| 226 | return result | 226 | return result | ||
| 227 | 227 | ||||
| 228 | def __gt__(self, other): | 228 | def __gt__(self, other): | ||
| 229 | self.validate_state_on_operation() | 229 | self.validate_state_on_operation() | ||
| 230 | other.validate_state_on_operation() | 230 | other.validate_state_on_operation() | ||
| 231 | return self.get_sum() > other.get_sum() | 231 | return self.get_sum() > other.get_sum() | ||
| 232 | 232 | ||||
| 233 | def __lt__(self, other): | 233 | def __lt__(self, other): | ||
| 234 | self.validate_state_on_operation() | 234 | self.validate_state_on_operation() | ||
| 235 | other.validate_state_on_operation() | 235 | other.validate_state_on_operation() | ||
| 236 | return self.get_sum() < other.get_sum() | 236 | return self.get_sum() < other.get_sum() | ||
| 237 | 237 | ||||
| 238 | 238 | ||||
| 239 | class ГоспожатаПоХимия: | 239 | class ГоспожатаПоХимия: | ||
| 240 | @staticmethod | 240 | @staticmethod | ||
| 241 | def calculate_molecular_mass(element): | 241 | def calculate_molecular_mass(element): | ||
| 242 | return sum([ord(i) for i in element]) | 242 | return sum([ord(i) for i in element]) | ||
| 243 | 243 | ||||
| 244 | def __init__(self): | 244 | def __init__(self): | ||
| 245 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 245 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 246 | self.timer = {} | 246 | self.timer = {} | ||
| 247 | 247 | ||||
| 248 | def apply_effect(self, target, effect): | 248 | def apply_effect(self, target, effect): | ||
| 249 | # creating a revrse function to reverse the changes after the effect expires | 249 | # creating a revrse function to reverse the changes after the effect expires | ||
| 250 | old_attributes = vars(target).copy() | 250 | old_attributes = vars(target).copy() | ||
| 251 | effect(target) | 251 | effect(target) | ||
| 252 | to_reverse = {} | 252 | to_reverse = {} | ||
| 253 | # set to store the new attributes added by the effect | 253 | # set to store the new attributes added by the effect | ||
| 254 | new_attribuets = set() | 254 | new_attribuets = set() | ||
| 255 | 255 | ||||
| 256 | for key, value in vars(target).items(): | 256 | for key, value in vars(target).items(): | ||
| 257 | old_value = 0 | 257 | old_value = 0 | ||
| 258 | if key in old_attributes.keys(): | 258 | if key in old_attributes.keys(): | ||
| 259 | old_value = old_attributes[key] | 259 | old_value = old_attributes[key] | ||
| 260 | else: | 260 | else: | ||
| 261 | new_attribuets.add(key) | 261 | new_attribuets.add(key) | ||
| 262 | if value != old_value: | 262 | if value != old_value: | ||
| 263 | to_reverse[key] = value - old_value | 263 | to_reverse[key] = value - old_value | ||
| 264 | 264 | ||||
| 265 | def reverse_effect(): | 265 | def reverse_effect(): | ||
| 266 | for key, value in to_reverse.items(): | 266 | for key, value in to_reverse.items(): | ||
| 267 | new_value = getattr(target, key) | 267 | new_value = getattr(target, key) | ||
| 268 | if new_value - value == 0 and key in new_attribuets: | 268 | if new_value - value == 0 and key in new_attribuets: | ||
| 269 | # the attribute is added in the function and when it is depleted it should be deleted | 269 | # the attribute is added in the function and when it is depleted it should be deleted | ||
| 270 | delattr(target, key) | 270 | delattr(target, key) | ||
| 271 | else: | 271 | else: | ||
| 272 | setattr(target, key, new_value - value) | 272 | setattr(target, key, new_value - value) | ||
| 273 | 273 | ||||
| 274 | return reverse_effect | 274 | return reverse_effect | ||
| 275 | 275 | ||||
| 276 | def apply(self, target, potion): | 276 | def apply(self, target, potion): | ||
| 277 | if potion.is_used(): | 277 | if potion.is_used(): | ||
| 278 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 278 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 279 | 279 | ||||
| 280 | if potion.is_component: # this is probably useless, but just to be sure | 280 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 281 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 281 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 282 | ordered_dir = sorted( | 282 | ordered_dir = sorted( | ||
| 283 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | 283 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | ||
| 284 | ) | 284 | ) | ||
| 285 | for key in ordered_dir: | 285 | for key in ordered_dir: | ||
| 286 | value = getattr(potion, key) | 286 | value = getattr(potion, key) | ||
| 287 | if type(value) is PotionEffect and not value.used: | 287 | if type(value) is PotionEffect and not value.used: | ||
| 288 | reverse_func = self.apply_effect(target, value) | 288 | reverse_func = self.apply_effect(target, value) | ||
| 289 | self.timer[reverse_func] = potion.duration | 289 | self.timer[reverse_func] = potion.duration | ||
| 290 | 290 | ||||
| 291 | def tick(self): | 291 | def tick(self): | ||
| 292 | to_iterate = self.timer.copy().items() | 292 | to_iterate = self.timer.copy().items() | ||
| 293 | for key, value in to_iterate: | 293 | for key, value in to_iterate: | ||
| 294 | self.timer[key] -= 1 | 294 | self.timer[key] -= 1 | ||
| 295 | if value <= 1: | 295 | if value <= 1: | ||
| 296 | key() | 296 | key() | ||
| 297 | self.timer.pop(key) | 297 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | class PotionEffect: | 9 | class PotionEffect: | ||
| 10 | @staticmethod | 10 | @staticmethod | ||
| 11 | def custom_round(value): | 11 | def custom_round(value): | ||
| 12 | result = round(value) | 12 | result = round(value) | ||
| 13 | if cmath.isclose( | 13 | if cmath.isclose( | ||
| 14 | result - value, 0.5 | 14 | result - value, 0.5 | ||
| 15 | ): # the edge case where x.5 should be rounded down | 15 | ): # the edge case where x.5 should be rounded down | ||
| 16 | result -= 1 | 16 | result -= 1 | ||
| 17 | return result | 17 | return result | ||
| 18 | 18 | ||||
| 19 | def __init__(self, func): | 19 | def __init__(self, func): | ||
| 20 | self.func = func | 20 | self.func = func | ||
| 21 | self.times = 1 | 21 | self.times = 1 | ||
| 22 | self.used = False | 22 | self.used = False | ||
| 23 | 23 | ||||
| 24 | def __iadd__(self, other): | 24 | def __iadd__(self, other): | ||
| 25 | self.times += other.times | 25 | self.times += other.times | ||
| 26 | return self | 26 | return self | ||
| 27 | 27 | ||||
| 28 | def __isub__(self, other): | 28 | def __isub__(self, other): | ||
| 29 | self.times -= other.times | 29 | self.times -= other.times | ||
| 30 | return self | 30 | return self | ||
| 31 | 31 | ||||
| 32 | def __imul__(self, other): | 32 | def __imul__(self, other): | ||
| 33 | self.times = PotionEffect.custom_round(self.times * other) | 33 | self.times = PotionEffect.custom_round(self.times * other) | ||
| 34 | return self | 34 | return self | ||
| 35 | 35 | ||||
| 36 | def __itruediv__(self, other): | 36 | def __itruediv__(self, other): | ||
| 37 | self.times = PotionEffect.custom_round(self.times / other) | 37 | self.times = PotionEffect.custom_round(self.times / other) | ||
| 38 | return self | 38 | return self | ||
| 39 | 39 | ||||
| 40 | def copy(self): | 40 | def copy(self): | ||
| 41 | result = PotionEffect(self.func) | 41 | result = PotionEffect(self.func) | ||
| 42 | result.times = self.times | 42 | result.times = self.times | ||
| 43 | result.used = self.used | 43 | result.used = self.used | ||
| 44 | return result | 44 | return result | ||
| 45 | 45 | ||||
| 46 | def __call__(self, target): | 46 | def __call__(self, target): | ||
| 47 | if self.used: | 47 | if self.used: | ||
| 48 | raise TypeError(EFFECT_ERROR_TEXT) | 48 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 49 | for _ in range(self.times): | 49 | for _ in range(self.times): | ||
| 50 | self.func(target) | 50 | self.func(target) | ||
| 51 | self.used = True | 51 | self.used = True | ||
| 52 | 52 | ||||
| 53 | 53 | ||||
| 54 | class meta(type): | 54 | class meta(type): | ||
| 55 | def __new__(cls, name, bases, attr_dict): | 55 | def __new__(cls, name, bases, attr_dict): | ||
| 56 | return type.__new__(cls, name, bases, attr_dict) | 56 | return type.__new__(cls, name, bases, attr_dict) | ||
| 57 | 57 | ||||
| 58 | 58 | ||||
| 59 | class Potion(metaclass=meta): | 59 | class Potion(metaclass=meta): | ||
| 60 | @staticmethod | 60 | @staticmethod | ||
| 61 | def fake_func(target): | 61 | def fake_func(target): | ||
| 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 63 | 63 | ||||
| 64 | @staticmethod | 64 | @staticmethod | ||
| 65 | def update_dict_with_class_funcs(result_dict): | 65 | def update_dict_with_class_funcs(result_dict): | ||
| 66 | result_dict.update( | 66 | result_dict.update( | ||
| 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | ||
| 68 | ) | 68 | ) | ||
| 69 | result_dict["__init__"] = Potion.__init__ | 69 | result_dict["__init__"] = Potion.__init__ | ||
| 70 | result_dict.pop("__weakref__") | 70 | result_dict.pop("__weakref__") | ||
| 71 | return result_dict | 71 | return result_dict | ||
| n | n | 72 | |||
| 73 | def validate_state_on_operation(self): | ||||
| 74 | if self.is_component: | ||||
| 75 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||||
| 76 | if self.is_used(): | ||||
| 77 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 72 | 78 | ||||
| 73 | def __init__(self, effects, duration): | 79 | def __init__(self, effects, duration): | ||
| 74 | for key, value in effects.items(): | 80 | for key, value in effects.items(): | ||
| 75 | setattr(self, key, PotionEffect(value)) | 81 | setattr(self, key, PotionEffect(value)) | ||
| 76 | self.duration = duration | 82 | self.duration = duration | ||
| 77 | self.is_component = False | 83 | self.is_component = False | ||
| 78 | 84 | ||||
| 79 | def is_used(self): | 85 | def is_used(self): | ||
| 80 | for key in dir(self): | 86 | for key in dir(self): | ||
| 81 | value = getattr(self, key) | 87 | value = getattr(self, key) | ||
| 82 | if type(value) is PotionEffect and not value.used: | 88 | if type(value) is PotionEffect and not value.used: | ||
| 83 | return False | 89 | return False | ||
| 84 | return True | 90 | return True | ||
| 85 | 91 | ||||
| 86 | def __getattribute__(self, name): | 92 | def __getattribute__(self, name): | ||
| 87 | result = object.__getattribute__(self, name) | 93 | result = object.__getattribute__(self, name) | ||
| 88 | if ( | 94 | if ( | ||
| 89 | object.__getattribute__(self, "is_component") | 95 | object.__getattribute__(self, "is_component") | ||
| 90 | and type(result) is PotionEffect | 96 | and type(result) is PotionEffect | ||
| 91 | ): | 97 | ): | ||
| 92 | fake_result = PotionEffect(Potion.fake_func) | 98 | fake_result = PotionEffect(Potion.fake_func) | ||
| 93 | fake_result.times = result.times | 99 | fake_result.times = result.times | ||
| 94 | fake_result.used = result.used | 100 | fake_result.used = result.used | ||
| 95 | return fake_result | 101 | return fake_result | ||
| 96 | 102 | ||||
| 97 | return result | 103 | return result | ||
| 98 | 104 | ||||
| 99 | def __add__(self, other): | 105 | def __add__(self, other): | ||
| n | 100 | if self.is_component or other.is_component: | n | 106 | self.validate_state_on_operation() |
| 101 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 107 | other.validate_state_on_operation() | ||
| 102 | if self.is_used() or other.is_used(): | ||||
| 103 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 104 | |||||
| 105 | # transfering the methods from the object on the left side to dict, | 108 | # transfering the methods from the object on the left side to dict, | ||
| 106 | # and if there are matching methods in the right side, we increace the intensity | 109 | # and if there are matching methods in the right side, we increace the intensity | ||
| 107 | result_dict = {} | 110 | result_dict = {} | ||
| 108 | for key in dir(self): | 111 | for key in dir(self): | ||
| 109 | value = getattr(self, key) | 112 | value = getattr(self, key) | ||
| 110 | if type(value) is PotionEffect and not value.used: | 113 | if type(value) is PotionEffect and not value.used: | ||
| 111 | result_dict[key] = value.copy() | 114 | result_dict[key] = value.copy() | ||
| 112 | if key in dir(other): | 115 | if key in dir(other): | ||
| 113 | other_value = getattr(other, key) | 116 | other_value = getattr(other, key) | ||
| 114 | if not other_value.used: | 117 | if not other_value.used: | ||
| 115 | result_dict[key] += other_value | 118 | result_dict[key] += other_value | ||
| 116 | 119 | ||||
| 117 | # transfering the methods that are only in the right side to the dict | 120 | # transfering the methods that are only in the right side to the dict | ||
| 118 | for key in dir(other): | 121 | for key in dir(other): | ||
| 119 | value = getattr(other, key) | 122 | value = getattr(other, key) | ||
| 120 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | 123 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | ||
| 121 | result_dict[key] = getattr(other, key).copy() | 124 | result_dict[key] = getattr(other, key).copy() | ||
| 122 | 125 | ||||
| 123 | Potion.update_dict_with_class_funcs(result_dict) | 126 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 124 | result = type("Potion", (object,), result_dict)( | 127 | result = type("Potion", (object,), result_dict)( | ||
| 125 | {}, max(self.duration, other.duration) | 128 | {}, max(self.duration, other.duration) | ||
| 126 | ) | 129 | ) | ||
| 127 | self.is_component = True | 130 | self.is_component = True | ||
| 128 | other.is_component = True | 131 | other.is_component = True | ||
| 129 | 132 | ||||
| 130 | return result | 133 | return result | ||
| 131 | 134 | ||||
| 132 | def __mul__(self, other): | 135 | def __mul__(self, other): | ||
| n | 133 | if self.is_component: | n | 136 | self.validate_state_on_operation() |
| 134 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||||
| 135 | if self.is_used(): | ||||
| 136 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 137 | result_dict = {} | 137 | result_dict = {} | ||
| 138 | # transfering the methods from the object to the dict and multiply them with the number | 138 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 139 | for key in dir(self): | 139 | for key in dir(self): | ||
| 140 | value = getattr(self, key) | 140 | value = getattr(self, key) | ||
| 141 | if type(value) is PotionEffect: | 141 | if type(value) is PotionEffect: | ||
| 142 | result_dict[key] = value.copy() | 142 | result_dict[key] = value.copy() | ||
| 143 | result_dict[key] *= other | 143 | result_dict[key] *= other | ||
| 144 | 144 | ||||
| 145 | Potion.update_dict_with_class_funcs(result_dict) | 145 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 146 | 146 | ||||
| 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 148 | self.is_component = True | 148 | self.is_component = True | ||
| 149 | 149 | ||||
| 150 | return result | 150 | return result | ||
| 151 | 151 | ||||
| 152 | def __sub__(self, other): | 152 | def __sub__(self, other): | ||
| n | 153 | if self.is_component or other.is_component: | n | 153 | self.validate_state_on_operation() |
| 154 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 154 | other.validate_state_on_operation() | ||
| 155 | if self.is_used() or other.is_used(): | ||||
| 156 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 157 | result_dict = {} | 155 | result_dict = {} | ||
| 158 | # validation that the substitution is valid | 156 | # validation that the substitution is valid | ||
| 159 | for key in dir(other): | 157 | for key in dir(other): | ||
| 160 | value = getattr(other, key) | 158 | value = getattr(other, key) | ||
| 161 | if type(value) is PotionEffect and not key in dir(self): | 159 | if type(value) is PotionEffect and not key in dir(self): | ||
| 162 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 160 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 163 | # transfering the methods from the object on the left side to dict, | 161 | # transfering the methods from the object on the left side to dict, | ||
| 164 | # and if there are matching methods in the right side, we subtract their intensity | 162 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 165 | for key in dir(self): | 163 | for key in dir(self): | ||
| 166 | value = getattr(self, key) | 164 | value = getattr(self, key) | ||
| 167 | if not type(value) is PotionEffect: | 165 | if not type(value) is PotionEffect: | ||
| 168 | continue | 166 | continue | ||
| 169 | result_dict[key] = value.copy() | 167 | result_dict[key] = value.copy() | ||
| 170 | if key in dir(other): | 168 | if key in dir(other): | ||
| 171 | other_value = getattr(other, key) | 169 | other_value = getattr(other, key) | ||
| 172 | if not other_value.used: | 170 | if not other_value.used: | ||
| 173 | result_dict[key] -= getattr(other, key) | 171 | result_dict[key] -= getattr(other, key) | ||
| 174 | if result_dict[key].times <= 0: | 172 | if result_dict[key].times <= 0: | ||
| 175 | result_dict.pop(key) | 173 | result_dict.pop(key) | ||
| 176 | 174 | ||||
| 177 | Potion.update_dict_with_class_funcs(result_dict) | 175 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 178 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 176 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 179 | self.is_component = True | 177 | self.is_component = True | ||
| 180 | other.is_component = True | 178 | other.is_component = True | ||
| 181 | return result | 179 | return result | ||
| 182 | 180 | ||||
| 183 | def __truediv__(self, other): | 181 | def __truediv__(self, other): | ||
| n | 184 | if self.is_component: | n | 182 | self.validate_state_on_operation() |
| 185 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||||
| 186 | if self.is_used(): | ||||
| 187 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 188 | # spliting the object into equal parts, where the intensity is devided by the number | 183 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 189 | result_list = [] | 184 | result_list = [] | ||
| 190 | for _ in range(other): | 185 | for _ in range(other): | ||
| 191 | result_dict = {} | 186 | result_dict = {} | ||
| 192 | for key in dir(self): | 187 | for key in dir(self): | ||
| 193 | value = getattr(self, key) | 188 | value = getattr(self, key) | ||
| 194 | if type(value) is PotionEffect: | 189 | if type(value) is PotionEffect: | ||
| 195 | result_dict[key] = value.copy() | 190 | result_dict[key] = value.copy() | ||
| 196 | result_dict[key] /= other | 191 | result_dict[key] /= other | ||
| 197 | 192 | ||||
| 198 | Potion.update_dict_with_class_funcs(result_dict) | 193 | Potion.update_dict_with_class_funcs(result_dict) | ||
| 199 | result = meta("Potion", (object,), result_dict)({}, self.duration) | 194 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 200 | result_list.append(result) | 195 | result_list.append(result) | ||
| 201 | 196 | ||||
| 202 | self.is_component = True | 197 | self.is_component = True | ||
| 203 | return tuple(result_list) | 198 | return tuple(result_list) | ||
| 204 | 199 | ||||
| 205 | def __eq__(self, other): | 200 | def __eq__(self, other): | ||
| n | n | 201 | self.validate_state_on_operation() | ||
| 202 | other.validate_state_on_operation() | ||||
| 203 | |||||
| 206 | for key in dir(self): | 204 | for key in dir(self): | ||
| 207 | value = getattr(self, key) | 205 | value = getattr(self, key) | ||
| 208 | if type(value) is PotionEffect: | 206 | if type(value) is PotionEffect: | ||
| 209 | if not key in dir(other): | 207 | if not key in dir(other): | ||
| 210 | return False | 208 | return False | ||
| 211 | other_value = getattr(other, key) | 209 | other_value = getattr(other, key) | ||
| 212 | if value.times != other_value.times or value.used != other_value.used: | 210 | if value.times != other_value.times or value.used != other_value.used: | ||
| 213 | return False | 211 | return False | ||
| 214 | 212 | ||||
| 215 | for key in dir(other): | 213 | for key in dir(other): | ||
| 216 | value = getattr(other, key) | 214 | value = getattr(other, key) | ||
| 217 | if type(value) is PotionEffect and not key in dir(self): | 215 | if type(value) is PotionEffect and not key in dir(self): | ||
| 218 | return False | 216 | return False | ||
| 219 | 217 | ||||
| 220 | return True | 218 | return True | ||
| 221 | 219 | ||||
| 222 | def get_sum(self): | 220 | def get_sum(self): | ||
| 223 | result = 0 | 221 | result = 0 | ||
| 224 | for key in dir(self): | 222 | for key in dir(self): | ||
| 225 | value = getattr(self, key) | 223 | value = getattr(self, key) | ||
| 226 | if type(value) is PotionEffect: | 224 | if type(value) is PotionEffect: | ||
| 227 | result += value.times | 225 | result += value.times | ||
| 228 | return result | 226 | return result | ||
| 229 | 227 | ||||
| 230 | def __gt__(self, other): | 228 | def __gt__(self, other): | ||
| n | n | 229 | self.validate_state_on_operation() | ||
| 230 | other.validate_state_on_operation() | ||||
| 231 | return self.get_sum() > other.get_sum() | 231 | return self.get_sum() > other.get_sum() | ||
| 232 | 232 | ||||
| 233 | def __lt__(self, other): | 233 | def __lt__(self, other): | ||
| n | n | 234 | self.validate_state_on_operation() | ||
| 235 | other.validate_state_on_operation() | ||||
| 234 | return self.get_sum() < other.get_sum() | 236 | return self.get_sum() < other.get_sum() | ||
| 235 | 237 | ||||
| 236 | 238 | ||||
| 237 | class ГоспожатаПоХимия: | 239 | class ГоспожатаПоХимия: | ||
| 238 | @staticmethod | 240 | @staticmethod | ||
| 239 | def calculate_molecular_mass(element): | 241 | def calculate_molecular_mass(element): | ||
| 240 | return sum([ord(i) for i in element]) | 242 | return sum([ord(i) for i in element]) | ||
| 241 | 243 | ||||
| 242 | def __init__(self): | 244 | def __init__(self): | ||
| 243 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 245 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 244 | self.timer = {} | 246 | self.timer = {} | ||
| 245 | 247 | ||||
| 246 | def apply_effect(self, target, effect): | 248 | def apply_effect(self, target, effect): | ||
| 247 | # creating a revrse function to reverse the changes after the effect expires | 249 | # creating a revrse function to reverse the changes after the effect expires | ||
| 248 | old_attributes = vars(target).copy() | 250 | old_attributes = vars(target).copy() | ||
| 249 | effect(target) | 251 | effect(target) | ||
| 250 | to_reverse = {} | 252 | to_reverse = {} | ||
| n | n | 253 | # set to store the new attributes added by the effect | ||
| 254 | new_attribuets = set() | ||||
| 251 | 255 | ||||
| n | n | 256 | for key, value in vars(target).items(): | ||
| 257 | old_value = 0 | ||||
| 252 | for key, value in old_attributes.items(): | 258 | if key in old_attributes.keys(): | ||
| 253 | new_value = getattr(target, key) | 259 | old_value = old_attributes[key] | ||
| 254 | if new_value != value: | 260 | else: | ||
| 261 | new_attribuets.add(key) | ||||
| 262 | if value != old_value: | ||||
| 255 | to_reverse[key] = new_value - value | 263 | to_reverse[key] = value - old_value | ||
| 256 | 264 | ||||
| 257 | def reverse_effect(): | 265 | def reverse_effect(): | ||
| 258 | for key, value in to_reverse.items(): | 266 | for key, value in to_reverse.items(): | ||
| 259 | new_value = getattr(target, key) | 267 | new_value = getattr(target, key) | ||
| t | t | 268 | if new_value - value == 0 and key in new_attribuets: | ||
| 269 | # the attribute is added in the function and when it is depleted it should be deleted | ||||
| 270 | delattr(target, key) | ||||
| 271 | else: | ||||
| 260 | setattr(target, key, new_value - value) | 272 | setattr(target, key, new_value - value) | ||
| 261 | 273 | ||||
| 262 | return reverse_effect | 274 | return reverse_effect | ||
| 263 | 275 | ||||
| 264 | def apply(self, target, potion): | 276 | def apply(self, target, potion): | ||
| 265 | if potion.is_used(): | 277 | if potion.is_used(): | ||
| 266 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 278 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 267 | 279 | ||||
| 268 | if potion.is_component: # this is probably useless, but just to be sure | 280 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 269 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 281 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 270 | ordered_dir = sorted( | 282 | ordered_dir = sorted( | ||
| 271 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | 283 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | ||
| 272 | ) | 284 | ) | ||
| 273 | for key in ordered_dir: | 285 | for key in ordered_dir: | ||
| 274 | value = getattr(potion, key) | 286 | value = getattr(potion, key) | ||
| 275 | if type(value) is PotionEffect and not value.used: | 287 | if type(value) is PotionEffect and not value.used: | ||
| 276 | reverse_func = self.apply_effect(target, value) | 288 | reverse_func = self.apply_effect(target, value) | ||
| 277 | self.timer[reverse_func] = potion.duration | 289 | self.timer[reverse_func] = potion.duration | ||
| 278 | 290 | ||||
| 279 | def tick(self): | 291 | def tick(self): | ||
| 280 | to_iterate = self.timer.copy().items() | 292 | to_iterate = self.timer.copy().items() | ||
| 281 | for key, value in to_iterate: | 293 | for key, value in to_iterate: | ||
| 282 | self.timer[key] -= 1 | 294 | self.timer[key] -= 1 | ||
| 283 | if value <= 1: | 295 | if value <= 1: | ||
| 284 | key() | 296 | key() | ||
| 285 | self.timer.pop(key) | 297 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| n | 9 | def fake_func(target): | n | ||
| 10 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||||
| 11 | |||||
| 12 | |||||
| 13 | def calculate_molecular_mass(element): | ||||
| 14 | return sum([ord(i) for i in element]) | ||||
| 15 | |||||
| 16 | |||||
| 17 | def custom_round(value): | ||||
| 18 | result = round(value) | ||||
| 19 | if cmath.isclose( | ||||
| 20 | result - value, 0.5 | ||||
| 21 | ): # the edge case where x.5 should be rounded down | ||||
| 22 | result -= 1 | ||||
| 23 | return result | ||||
| 24 | |||||
| 25 | |||||
| 26 | class PotionEffect: | 9 | class PotionEffect: | ||
| n | n | 10 | @staticmethod | ||
| 11 | def custom_round(value): | ||||
| 12 | result = round(value) | ||||
| 13 | if cmath.isclose( | ||||
| 14 | result - value, 0.5 | ||||
| 15 | ): # the edge case where x.5 should be rounded down | ||||
| 16 | result -= 1 | ||||
| 17 | return result | ||||
| 18 | |||||
| 27 | def __init__(self, func): | 19 | def __init__(self, func): | ||
| 28 | self.func = func | 20 | self.func = func | ||
| 29 | self.times = 1 | 21 | self.times = 1 | ||
| 30 | self.used = False | 22 | self.used = False | ||
| 31 | 23 | ||||
| 32 | def __iadd__(self, other): | 24 | def __iadd__(self, other): | ||
| 33 | self.times += other.times | 25 | self.times += other.times | ||
| 34 | return self | 26 | return self | ||
| 35 | 27 | ||||
| 36 | def __isub__(self, other): | 28 | def __isub__(self, other): | ||
| 37 | self.times -= other.times | 29 | self.times -= other.times | ||
| 38 | return self | 30 | return self | ||
| 39 | 31 | ||||
| 40 | def __imul__(self, other): | 32 | def __imul__(self, other): | ||
| n | 41 | self.times = custom_round(self.times * other) | n | 33 | self.times = PotionEffect.custom_round(self.times * other) |
| 42 | return self | 34 | return self | ||
| 43 | 35 | ||||
| 44 | def __itruediv__(self, other): | 36 | def __itruediv__(self, other): | ||
| n | 45 | self.times = custom_round(self.times / other) | n | 37 | self.times = PotionEffect.custom_round(self.times / other) |
| 46 | return self | 38 | return self | ||
| 47 | 39 | ||||
| 48 | def copy(self): | 40 | def copy(self): | ||
| 49 | result = PotionEffect(self.func) | 41 | result = PotionEffect(self.func) | ||
| 50 | result.times = self.times | 42 | result.times = self.times | ||
| 51 | result.used = self.used | 43 | result.used = self.used | ||
| 52 | return result | 44 | return result | ||
| 53 | 45 | ||||
| 54 | def __call__(self, target): | 46 | def __call__(self, target): | ||
| 55 | if self.used: | 47 | if self.used: | ||
| 56 | raise TypeError(EFFECT_ERROR_TEXT) | 48 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 57 | for _ in range(self.times): | 49 | for _ in range(self.times): | ||
| 58 | self.func(target) | 50 | self.func(target) | ||
| 59 | self.used = True | 51 | self.used = True | ||
| 60 | 52 | ||||
| 61 | 53 | ||||
| 62 | class meta(type): | 54 | class meta(type): | ||
| 63 | def __new__(cls, name, bases, attr_dict): | 55 | def __new__(cls, name, bases, attr_dict): | ||
| n | 64 | attr_dict["is_component"] = False | n | ||
| 65 | return type.__new__(cls, name, bases, attr_dict) | 56 | return type.__new__(cls, name, bases, attr_dict) | ||
| 66 | 57 | ||||
| 67 | 58 | ||||
| 68 | class Potion(metaclass=meta): | 59 | class Potion(metaclass=meta): | ||
| n | n | 60 | @staticmethod | ||
| 61 | def fake_func(target): | ||||
| 62 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||||
| 63 | |||||
| 64 | @staticmethod | ||||
| 65 | def update_dict_with_class_funcs(result_dict): | ||||
| 66 | result_dict.update( | ||||
| 67 | {key: value for key, value in vars(Potion).items() if not key in dir(type)} | ||||
| 68 | ) | ||||
| 69 | result_dict["__init__"] = Potion.__init__ | ||||
| 70 | result_dict.pop("__weakref__") | ||||
| 71 | return result_dict | ||||
| 72 | |||||
| 69 | def __init__(self, effects, duration): | 73 | def __init__(self, effects, duration): | ||
| 70 | for key, value in effects.items(): | 74 | for key, value in effects.items(): | ||
| 71 | setattr(self, key, PotionEffect(value)) | 75 | setattr(self, key, PotionEffect(value)) | ||
| 72 | self.duration = duration | 76 | self.duration = duration | ||
| n | n | 77 | self.is_component = False | ||
| 73 | 78 | ||||
| 74 | def is_used(self): | 79 | def is_used(self): | ||
| 75 | for key in dir(self): | 80 | for key in dir(self): | ||
| 76 | value = getattr(self, key) | 81 | value = getattr(self, key) | ||
| 77 | if type(value) is PotionEffect and not value.used: | 82 | if type(value) is PotionEffect and not value.used: | ||
| 78 | return False | 83 | return False | ||
| 79 | return True | 84 | return True | ||
| 80 | 85 | ||||
| 81 | def __getattribute__(self, name): | 86 | def __getattribute__(self, name): | ||
| 82 | result = object.__getattribute__(self, name) | 87 | result = object.__getattribute__(self, name) | ||
| 83 | if ( | 88 | if ( | ||
| 84 | object.__getattribute__(self, "is_component") | 89 | object.__getattribute__(self, "is_component") | ||
| 85 | and type(result) is PotionEffect | 90 | and type(result) is PotionEffect | ||
| 86 | ): | 91 | ): | ||
| n | 87 | fake_result = PotionEffect(fake_func) | n | 92 | fake_result = PotionEffect(Potion.fake_func) |
| 88 | fake_result.times = result.times | 93 | fake_result.times = result.times | ||
| 89 | fake_result.used = result.used | 94 | fake_result.used = result.used | ||
| 90 | return fake_result | 95 | return fake_result | ||
| 91 | 96 | ||||
| 92 | return result | 97 | return result | ||
| 93 | 98 | ||||
| 94 | def __add__(self, other): | 99 | def __add__(self, other): | ||
| 95 | if self.is_component or other.is_component: | 100 | if self.is_component or other.is_component: | ||
| 96 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 101 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 97 | if self.is_used() or other.is_used(): | 102 | if self.is_used() or other.is_used(): | ||
| 98 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 103 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 99 | 104 | ||||
| 100 | # transfering the methods from the object on the left side to dict, | 105 | # transfering the methods from the object on the left side to dict, | ||
| 101 | # and if there are matching methods in the right side, we increace the intensity | 106 | # and if there are matching methods in the right side, we increace the intensity | ||
| 102 | result_dict = {} | 107 | result_dict = {} | ||
| 103 | for key in dir(self): | 108 | for key in dir(self): | ||
| 104 | value = getattr(self, key) | 109 | value = getattr(self, key) | ||
| 105 | if type(value) is PotionEffect and not value.used: | 110 | if type(value) is PotionEffect and not value.used: | ||
| 106 | result_dict[key] = value.copy() | 111 | result_dict[key] = value.copy() | ||
| 107 | if key in dir(other): | 112 | if key in dir(other): | ||
| 108 | other_value = getattr(other, key) | 113 | other_value = getattr(other, key) | ||
| 109 | if not other_value.used: | 114 | if not other_value.used: | ||
| 110 | result_dict[key] += other_value | 115 | result_dict[key] += other_value | ||
| 111 | 116 | ||||
| 112 | # transfering the methods that are only in the right side to the dict | 117 | # transfering the methods that are only in the right side to the dict | ||
| 113 | for key in dir(other): | 118 | for key in dir(other): | ||
| 114 | value = getattr(other, key) | 119 | value = getattr(other, key) | ||
| 115 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | 120 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | ||
| 116 | result_dict[key] = getattr(other, key).copy() | 121 | result_dict[key] = getattr(other, key).copy() | ||
| 117 | 122 | ||||
| n | 118 | result_dict["duration"] = max(self.duration, other.duration) | n | 123 | Potion.update_dict_with_class_funcs(result_dict) |
| 119 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 124 | result = type("Potion", (object,), result_dict)( | ||
| 125 | {}, max(self.duration, other.duration) | ||||
| 126 | ) | ||||
| 120 | self.is_component = True | 127 | self.is_component = True | ||
| 121 | other.is_component = True | 128 | other.is_component = True | ||
| 122 | 129 | ||||
| 123 | return result | 130 | return result | ||
| 124 | 131 | ||||
| 125 | def __mul__(self, other): | 132 | def __mul__(self, other): | ||
| 126 | if self.is_component: | 133 | if self.is_component: | ||
| 127 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 134 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 128 | if self.is_used(): | 135 | if self.is_used(): | ||
| 129 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 136 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 130 | result_dict = {} | 137 | result_dict = {} | ||
| 131 | # transfering the methods from the object to the dict and multiply them with the number | 138 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 132 | for key in dir(self): | 139 | for key in dir(self): | ||
| 133 | value = getattr(self, key) | 140 | value = getattr(self, key) | ||
| 134 | if type(value) is PotionEffect: | 141 | if type(value) is PotionEffect: | ||
| 135 | result_dict[key] = value.copy() | 142 | result_dict[key] = value.copy() | ||
| 136 | result_dict[key] *= other | 143 | result_dict[key] *= other | ||
| 137 | 144 | ||||
| n | 138 | result_dict["duration"] = self.duration | n | 145 | Potion.update_dict_with_class_funcs(result_dict) |
| 146 | |||||
| 139 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 147 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 140 | self.is_component = True | 148 | self.is_component = True | ||
| 141 | 149 | ||||
| 142 | return result | 150 | return result | ||
| 143 | 151 | ||||
| 144 | def __sub__(self, other): | 152 | def __sub__(self, other): | ||
| 145 | if self.is_component or other.is_component: | 153 | if self.is_component or other.is_component: | ||
| 146 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 154 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 147 | if self.is_used() or other.is_used(): | 155 | if self.is_used() or other.is_used(): | ||
| 148 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 156 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 149 | result_dict = {} | 157 | result_dict = {} | ||
| 150 | # validation that the substitution is valid | 158 | # validation that the substitution is valid | ||
| 151 | for key in dir(other): | 159 | for key in dir(other): | ||
| 152 | value = getattr(other, key) | 160 | value = getattr(other, key) | ||
| 153 | if type(value) is PotionEffect and not key in dir(self): | 161 | if type(value) is PotionEffect and not key in dir(self): | ||
| 154 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 162 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 155 | # transfering the methods from the object on the left side to dict, | 163 | # transfering the methods from the object on the left side to dict, | ||
| 156 | # and if there are matching methods in the right side, we subtract their intensity | 164 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 157 | for key in dir(self): | 165 | for key in dir(self): | ||
| 158 | value = getattr(self, key) | 166 | value = getattr(self, key) | ||
| n | 159 | if type(value) is PotionEffect: | n | 167 | if not type(value) is PotionEffect: |
| 168 | continue | ||||
| 160 | result_dict[key] = value.copy() | 169 | result_dict[key] = value.copy() | ||
| 161 | if key in dir(other): | 170 | if key in dir(other): | ||
| 162 | other_value = getattr(other, key) | 171 | other_value = getattr(other, key) | ||
| 163 | if not other_value.used: | 172 | if not other_value.used: | ||
| 164 | result_dict[key] -= getattr(other, key) | 173 | result_dict[key] -= getattr(other, key) | ||
| 165 | if result_dict[key].times <= 0: | 174 | if result_dict[key].times <= 0: | ||
| 166 | result_dict.pop(key) | 175 | result_dict.pop(key) | ||
| 167 | 176 | ||||
| n | 168 | result_dict["duration"] = self.duration | n | 177 | Potion.update_dict_with_class_funcs(result_dict) |
| 169 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 178 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 170 | self.is_component = True | 179 | self.is_component = True | ||
| 171 | other.is_component = True | 180 | other.is_component = True | ||
| 172 | return result | 181 | return result | ||
| 173 | 182 | ||||
| 174 | def __truediv__(self, other): | 183 | def __truediv__(self, other): | ||
| 175 | if self.is_component: | 184 | if self.is_component: | ||
| 176 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 185 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 177 | if self.is_used(): | 186 | if self.is_used(): | ||
| 178 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 187 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 179 | # spliting the object into equal parts, where the intensity is devided by the number | 188 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 180 | result_list = [] | 189 | result_list = [] | ||
| 181 | for _ in range(other): | 190 | for _ in range(other): | ||
| 182 | result_dict = {} | 191 | result_dict = {} | ||
| 183 | for key in dir(self): | 192 | for key in dir(self): | ||
| 184 | value = getattr(self, key) | 193 | value = getattr(self, key) | ||
| 185 | if type(value) is PotionEffect: | 194 | if type(value) is PotionEffect: | ||
| 186 | result_dict[key] = value.copy() | 195 | result_dict[key] = value.copy() | ||
| 187 | result_dict[key] /= other | 196 | result_dict[key] /= other | ||
| 188 | 197 | ||||
| n | 189 | result_dict["duration"] = self.duration | n | 198 | Potion.update_dict_with_class_funcs(result_dict) |
| 190 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 199 | result = meta("Potion", (object,), result_dict)({}, self.duration) | ||
| 191 | result_list.append(result) | 200 | result_list.append(result) | ||
| 192 | 201 | ||||
| 193 | self.is_component = True | 202 | self.is_component = True | ||
| 194 | return tuple(result_list) | 203 | return tuple(result_list) | ||
| 195 | 204 | ||||
| 196 | def __eq__(self, other): | 205 | def __eq__(self, other): | ||
| 197 | for key in dir(self): | 206 | for key in dir(self): | ||
| 198 | value = getattr(self, key) | 207 | value = getattr(self, key) | ||
| 199 | if type(value) is PotionEffect: | 208 | if type(value) is PotionEffect: | ||
| 200 | if not key in dir(other): | 209 | if not key in dir(other): | ||
| 201 | return False | 210 | return False | ||
| 202 | other_value = getattr(other, key) | 211 | other_value = getattr(other, key) | ||
| 203 | if value.times != other_value.times or value.used != other_value.used: | 212 | if value.times != other_value.times or value.used != other_value.used: | ||
| 204 | return False | 213 | return False | ||
| 205 | 214 | ||||
| 206 | for key in dir(other): | 215 | for key in dir(other): | ||
| 207 | value = getattr(other, key) | 216 | value = getattr(other, key) | ||
| 208 | if type(value) is PotionEffect and not key in dir(self): | 217 | if type(value) is PotionEffect and not key in dir(self): | ||
| 209 | return False | 218 | return False | ||
| 210 | 219 | ||||
| 211 | return True | 220 | return True | ||
| 212 | 221 | ||||
| 213 | def get_sum(self): | 222 | def get_sum(self): | ||
| 214 | result = 0 | 223 | result = 0 | ||
| 215 | for key in dir(self): | 224 | for key in dir(self): | ||
| 216 | value = getattr(self, key) | 225 | value = getattr(self, key) | ||
| 217 | if type(value) is PotionEffect: | 226 | if type(value) is PotionEffect: | ||
| 218 | result += value.times | 227 | result += value.times | ||
| 219 | return result | 228 | return result | ||
| 220 | 229 | ||||
| 221 | def __gt__(self, other): | 230 | def __gt__(self, other): | ||
| 222 | return self.get_sum() > other.get_sum() | 231 | return self.get_sum() > other.get_sum() | ||
| 223 | 232 | ||||
| 224 | def __lt__(self, other): | 233 | def __lt__(self, other): | ||
| 225 | return self.get_sum() < other.get_sum() | 234 | return self.get_sum() < other.get_sum() | ||
| 226 | 235 | ||||
| 227 | 236 | ||||
| 228 | class ГоспожатаПоХимия: | 237 | class ГоспожатаПоХимия: | ||
| n | n | 238 | @staticmethod | ||
| 239 | def calculate_molecular_mass(element): | ||||
| 240 | return sum([ord(i) for i in element]) | ||||
| 241 | |||||
| 229 | def __init__(self): | 242 | def __init__(self): | ||
| 230 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 243 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 231 | self.timer = {} | 244 | self.timer = {} | ||
| 232 | 245 | ||||
| 233 | def apply_effect(self, target, effect): | 246 | def apply_effect(self, target, effect): | ||
| 234 | # creating a revrse function to reverse the changes after the effect expires | 247 | # creating a revrse function to reverse the changes after the effect expires | ||
| 235 | old_attributes = vars(target).copy() | 248 | old_attributes = vars(target).copy() | ||
| 236 | effect(target) | 249 | effect(target) | ||
| 237 | to_reverse = {} | 250 | to_reverse = {} | ||
| n | n | 251 | |||
| 238 | for key, value in old_attributes.items(): | 252 | for key, value in old_attributes.items(): | ||
| 239 | new_value = getattr(target, key) | 253 | new_value = getattr(target, key) | ||
| 240 | if new_value != value: | 254 | if new_value != value: | ||
| 241 | to_reverse[key] = new_value - value | 255 | to_reverse[key] = new_value - value | ||
| 242 | 256 | ||||
| 243 | def reverse_effect(): | 257 | def reverse_effect(): | ||
| 244 | for key, value in to_reverse.items(): | 258 | for key, value in to_reverse.items(): | ||
| 245 | new_value = getattr(target, key) | 259 | new_value = getattr(target, key) | ||
| 246 | setattr(target, key, new_value - value) | 260 | setattr(target, key, new_value - value) | ||
| 247 | 261 | ||||
| 248 | return reverse_effect | 262 | return reverse_effect | ||
| 249 | 263 | ||||
| 250 | def apply(self, target, potion): | 264 | def apply(self, target, potion): | ||
| 251 | if potion.is_used(): | 265 | if potion.is_used(): | ||
| 252 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 266 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 253 | 267 | ||||
| 254 | if potion.is_component: # this is probably useless, but just to be sure | 268 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 255 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 269 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| t | 256 | ordered_dir = sorted(dir(potion), key=calculate_molecular_mass, reverse=True) | t | 270 | ordered_dir = sorted( |
| 271 | dir(potion), key=ГоспожатаПоХимия.calculate_molecular_mass, reverse=True | ||||
| 272 | ) | ||||
| 257 | for key in ordered_dir: | 273 | for key in ordered_dir: | ||
| 258 | value = getattr(potion, key) | 274 | value = getattr(potion, key) | ||
| 259 | if type(value) is PotionEffect and not value.used: | 275 | if type(value) is PotionEffect and not value.used: | ||
| 260 | reverse_func = self.apply_effect(target, value) | 276 | reverse_func = self.apply_effect(target, value) | ||
| 261 | self.timer[reverse_func] = potion.duration | 277 | self.timer[reverse_func] = potion.duration | ||
| 262 | 278 | ||||
| 263 | def tick(self): | 279 | def tick(self): | ||
| 264 | to_iterate = self.timer.copy().items() | 280 | to_iterate = self.timer.copy().items() | ||
| 265 | for key, value in to_iterate: | 281 | for key, value in to_iterate: | ||
| 266 | self.timer[key] -= 1 | 282 | self.timer[key] -= 1 | ||
| 267 | if value <= 1: | 283 | if value <= 1: | ||
| 268 | key() | 284 | key() | ||
| 269 | self.timer.pop(key) | 285 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | def fake_func(target): | 9 | def fake_func(target): | ||
| 10 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 10 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| n | n | 11 | |||
| 12 | |||||
| 13 | def calculate_molecular_mass(element): | ||||
| 14 | return sum([ord(i) for i in element]) | ||||
| 11 | 15 | ||||
| 12 | 16 | ||||
| 13 | def custom_round(value): | 17 | def custom_round(value): | ||
| 14 | result = round(value) | 18 | result = round(value) | ||
| 15 | if cmath.isclose( | 19 | if cmath.isclose( | ||
| 16 | result - value, 0.5 | 20 | result - value, 0.5 | ||
| 17 | ): # the edge case where x.5 should be rounded down | 21 | ): # the edge case where x.5 should be rounded down | ||
| 18 | result -= 1 | 22 | result -= 1 | ||
| 19 | return result | 23 | return result | ||
| 20 | 24 | ||||
| 21 | 25 | ||||
| 22 | class PotionEffect: | 26 | class PotionEffect: | ||
| 23 | def __init__(self, func): | 27 | def __init__(self, func): | ||
| 24 | self.func = func | 28 | self.func = func | ||
| 25 | self.times = 1 | 29 | self.times = 1 | ||
| 26 | self.used = False | 30 | self.used = False | ||
| 27 | 31 | ||||
| 28 | def __iadd__(self, other): | 32 | def __iadd__(self, other): | ||
| 29 | self.times += other.times | 33 | self.times += other.times | ||
| 30 | return self | 34 | return self | ||
| 31 | 35 | ||||
| 32 | def __isub__(self, other): | 36 | def __isub__(self, other): | ||
| 33 | self.times -= other.times | 37 | self.times -= other.times | ||
| 34 | return self | 38 | return self | ||
| 35 | 39 | ||||
| 36 | def __imul__(self, other): | 40 | def __imul__(self, other): | ||
| 37 | self.times = custom_round(self.times * other) | 41 | self.times = custom_round(self.times * other) | ||
| 38 | return self | 42 | return self | ||
| 39 | 43 | ||||
| 40 | def __itruediv__(self, other): | 44 | def __itruediv__(self, other): | ||
| 41 | self.times = custom_round(self.times / other) | 45 | self.times = custom_round(self.times / other) | ||
| 42 | return self | 46 | return self | ||
| 43 | 47 | ||||
| 44 | def copy(self): | 48 | def copy(self): | ||
| 45 | result = PotionEffect(self.func) | 49 | result = PotionEffect(self.func) | ||
| 46 | result.times = self.times | 50 | result.times = self.times | ||
| 47 | result.used = self.used | 51 | result.used = self.used | ||
| 48 | return result | 52 | return result | ||
| 49 | 53 | ||||
| 50 | def __call__(self, target): | 54 | def __call__(self, target): | ||
| 51 | if self.used: | 55 | if self.used: | ||
| 52 | raise TypeError(EFFECT_ERROR_TEXT) | 56 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 53 | for _ in range(self.times): | 57 | for _ in range(self.times): | ||
| 54 | self.func(target) | 58 | self.func(target) | ||
| 55 | self.used = True | 59 | self.used = True | ||
| 56 | 60 | ||||
| 57 | 61 | ||||
| 58 | class meta(type): | 62 | class meta(type): | ||
| 59 | def __new__(cls, name, bases, attr_dict): | 63 | def __new__(cls, name, bases, attr_dict): | ||
| 60 | attr_dict["is_component"] = False | 64 | attr_dict["is_component"] = False | ||
| 61 | return type.__new__(cls, name, bases, attr_dict) | 65 | return type.__new__(cls, name, bases, attr_dict) | ||
| 62 | 66 | ||||
| 63 | 67 | ||||
| 64 | class Potion(metaclass=meta): | 68 | class Potion(metaclass=meta): | ||
| 65 | def __init__(self, effects, duration): | 69 | def __init__(self, effects, duration): | ||
| 66 | for key, value in effects.items(): | 70 | for key, value in effects.items(): | ||
| 67 | setattr(self, key, PotionEffect(value)) | 71 | setattr(self, key, PotionEffect(value)) | ||
| 68 | self.duration = duration | 72 | self.duration = duration | ||
| 69 | 73 | ||||
| 70 | def is_used(self): | 74 | def is_used(self): | ||
| 71 | for key in dir(self): | 75 | for key in dir(self): | ||
| 72 | value = getattr(self, key) | 76 | value = getattr(self, key) | ||
| 73 | if type(value) is PotionEffect and not value.used: | 77 | if type(value) is PotionEffect and not value.used: | ||
| 74 | return False | 78 | return False | ||
| 75 | return True | 79 | return True | ||
| 76 | 80 | ||||
| 77 | def __getattribute__(self, name): | 81 | def __getattribute__(self, name): | ||
| 78 | result = object.__getattribute__(self, name) | 82 | result = object.__getattribute__(self, name) | ||
| 79 | if ( | 83 | if ( | ||
| 80 | object.__getattribute__(self, "is_component") | 84 | object.__getattribute__(self, "is_component") | ||
| 81 | and type(result) is PotionEffect | 85 | and type(result) is PotionEffect | ||
| 82 | ): | 86 | ): | ||
| 83 | fake_result = PotionEffect(fake_func) | 87 | fake_result = PotionEffect(fake_func) | ||
| 84 | fake_result.times = result.times | 88 | fake_result.times = result.times | ||
| 85 | fake_result.used = result.used | 89 | fake_result.used = result.used | ||
| 86 | return fake_result | 90 | return fake_result | ||
| 87 | 91 | ||||
| 88 | return result | 92 | return result | ||
| 89 | 93 | ||||
| 90 | def __add__(self, other): | 94 | def __add__(self, other): | ||
| 91 | if self.is_component or other.is_component: | 95 | if self.is_component or other.is_component: | ||
| 92 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 96 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 93 | if self.is_used() or other.is_used(): | 97 | if self.is_used() or other.is_used(): | ||
| 94 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 98 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 95 | 99 | ||||
| 96 | # transfering the methods from the object on the left side to dict, | 100 | # transfering the methods from the object on the left side to dict, | ||
| 97 | # and if there are matching methods in the right side, we increace the intensity | 101 | # and if there are matching methods in the right side, we increace the intensity | ||
| 98 | result_dict = {} | 102 | result_dict = {} | ||
| 99 | for key in dir(self): | 103 | for key in dir(self): | ||
| 100 | value = getattr(self, key) | 104 | value = getattr(self, key) | ||
| 101 | if type(value) is PotionEffect and not value.used: | 105 | if type(value) is PotionEffect and not value.used: | ||
| 102 | result_dict[key] = value.copy() | 106 | result_dict[key] = value.copy() | ||
| 103 | if key in dir(other): | 107 | if key in dir(other): | ||
| 104 | other_value = getattr(other, key) | 108 | other_value = getattr(other, key) | ||
| 105 | if not other_value.used: | 109 | if not other_value.used: | ||
| 106 | result_dict[key] += other_value | 110 | result_dict[key] += other_value | ||
| 107 | 111 | ||||
| 108 | # transfering the methods that are only in the right side to the dict | 112 | # transfering the methods that are only in the right side to the dict | ||
| 109 | for key in dir(other): | 113 | for key in dir(other): | ||
| 110 | value = getattr(other, key) | 114 | value = getattr(other, key) | ||
| 111 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | 115 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | ||
| 112 | result_dict[key] = getattr(other, key).copy() | 116 | result_dict[key] = getattr(other, key).copy() | ||
| 113 | 117 | ||||
| 114 | result_dict["duration"] = max(self.duration, other.duration) | 118 | result_dict["duration"] = max(self.duration, other.duration) | ||
| 115 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 119 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 116 | self.is_component = True | 120 | self.is_component = True | ||
| 117 | other.is_component = True | 121 | other.is_component = True | ||
| 118 | 122 | ||||
| 119 | return result | 123 | return result | ||
| 120 | 124 | ||||
| 121 | def __mul__(self, other): | 125 | def __mul__(self, other): | ||
| 122 | if self.is_component: | 126 | if self.is_component: | ||
| 123 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 127 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 124 | if self.is_used(): | 128 | if self.is_used(): | ||
| 125 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 129 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 126 | result_dict = {} | 130 | result_dict = {} | ||
| 127 | # transfering the methods from the object to the dict and multiply them with the number | 131 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 128 | for key in dir(self): | 132 | for key in dir(self): | ||
| 129 | value = getattr(self, key) | 133 | value = getattr(self, key) | ||
| 130 | if type(value) is PotionEffect: | 134 | if type(value) is PotionEffect: | ||
| 131 | result_dict[key] = value.copy() | 135 | result_dict[key] = value.copy() | ||
| 132 | result_dict[key] *= other | 136 | result_dict[key] *= other | ||
| 133 | 137 | ||||
| 134 | result_dict["duration"] = self.duration | 138 | result_dict["duration"] = self.duration | ||
| 135 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 139 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 136 | self.is_component = True | 140 | self.is_component = True | ||
| 137 | 141 | ||||
| 138 | return result | 142 | return result | ||
| 139 | 143 | ||||
| 140 | def __sub__(self, other): | 144 | def __sub__(self, other): | ||
| 141 | if self.is_component or other.is_component: | 145 | if self.is_component or other.is_component: | ||
| 142 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 146 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 143 | if self.is_used() or other.is_used(): | 147 | if self.is_used() or other.is_used(): | ||
| 144 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 148 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 145 | result_dict = {} | 149 | result_dict = {} | ||
| 146 | # validation that the substitution is valid | 150 | # validation that the substitution is valid | ||
| 147 | for key in dir(other): | 151 | for key in dir(other): | ||
| 148 | value = getattr(other, key) | 152 | value = getattr(other, key) | ||
| 149 | if type(value) is PotionEffect and not key in dir(self): | 153 | if type(value) is PotionEffect and not key in dir(self): | ||
| 150 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 154 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 151 | # transfering the methods from the object on the left side to dict, | 155 | # transfering the methods from the object on the left side to dict, | ||
| 152 | # and if there are matching methods in the right side, we subtract their intensity | 156 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 153 | for key in dir(self): | 157 | for key in dir(self): | ||
| 154 | value = getattr(self, key) | 158 | value = getattr(self, key) | ||
| 155 | if type(value) is PotionEffect: | 159 | if type(value) is PotionEffect: | ||
| 156 | result_dict[key] = value.copy() | 160 | result_dict[key] = value.copy() | ||
| 157 | if key in dir(other): | 161 | if key in dir(other): | ||
| 158 | other_value = getattr(other, key) | 162 | other_value = getattr(other, key) | ||
| 159 | if not other_value.used: | 163 | if not other_value.used: | ||
| 160 | result_dict[key] -= getattr(other, key) | 164 | result_dict[key] -= getattr(other, key) | ||
| 161 | if result_dict[key].times <= 0: | 165 | if result_dict[key].times <= 0: | ||
| 162 | result_dict.pop(key) | 166 | result_dict.pop(key) | ||
| 163 | 167 | ||||
| 164 | result_dict["duration"] = self.duration | 168 | result_dict["duration"] = self.duration | ||
| 165 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 169 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 166 | self.is_component = True | 170 | self.is_component = True | ||
| 167 | other.is_component = True | 171 | other.is_component = True | ||
| 168 | return result | 172 | return result | ||
| 169 | 173 | ||||
| 170 | def __truediv__(self, other): | 174 | def __truediv__(self, other): | ||
| 171 | if self.is_component: | 175 | if self.is_component: | ||
| 172 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 176 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 173 | if self.is_used(): | 177 | if self.is_used(): | ||
| 174 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 178 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 175 | # spliting the object into equal parts, where the intensity is devided by the number | 179 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 176 | result_list = [] | 180 | result_list = [] | ||
| 177 | for _ in range(other): | 181 | for _ in range(other): | ||
| 178 | result_dict = {} | 182 | result_dict = {} | ||
| 179 | for key in dir(self): | 183 | for key in dir(self): | ||
| 180 | value = getattr(self, key) | 184 | value = getattr(self, key) | ||
| 181 | if type(value) is PotionEffect: | 185 | if type(value) is PotionEffect: | ||
| 182 | result_dict[key] = value.copy() | 186 | result_dict[key] = value.copy() | ||
| 183 | result_dict[key] /= other | 187 | result_dict[key] /= other | ||
| 184 | 188 | ||||
| 185 | result_dict["duration"] = self.duration | 189 | result_dict["duration"] = self.duration | ||
| 186 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 190 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 187 | result_list.append(result) | 191 | result_list.append(result) | ||
| 188 | 192 | ||||
| 189 | self.is_component = True | 193 | self.is_component = True | ||
| 190 | return tuple(result_list) | 194 | return tuple(result_list) | ||
| 191 | 195 | ||||
| 192 | def __eq__(self, other): | 196 | def __eq__(self, other): | ||
| 193 | for key in dir(self): | 197 | for key in dir(self): | ||
| 194 | value = getattr(self, key) | 198 | value = getattr(self, key) | ||
| 195 | if type(value) is PotionEffect: | 199 | if type(value) is PotionEffect: | ||
| 196 | if not key in dir(other): | 200 | if not key in dir(other): | ||
| 197 | return False | 201 | return False | ||
| 198 | other_value = getattr(other, key) | 202 | other_value = getattr(other, key) | ||
| 199 | if value.times != other_value.times or value.used != other_value.used: | 203 | if value.times != other_value.times or value.used != other_value.used: | ||
| 200 | return False | 204 | return False | ||
| 201 | 205 | ||||
| 202 | for key in dir(other): | 206 | for key in dir(other): | ||
| 203 | value = getattr(other, key) | 207 | value = getattr(other, key) | ||
| 204 | if type(value) is PotionEffect and not key in dir(self): | 208 | if type(value) is PotionEffect and not key in dir(self): | ||
| 205 | return False | 209 | return False | ||
| 206 | 210 | ||||
| 207 | return True | 211 | return True | ||
| 208 | 212 | ||||
| 209 | def get_sum(self): | 213 | def get_sum(self): | ||
| 210 | result = 0 | 214 | result = 0 | ||
| 211 | for key in dir(self): | 215 | for key in dir(self): | ||
| 212 | value = getattr(self, key) | 216 | value = getattr(self, key) | ||
| 213 | if type(value) is PotionEffect: | 217 | if type(value) is PotionEffect: | ||
| 214 | result += value.times | 218 | result += value.times | ||
| 215 | return result | 219 | return result | ||
| 216 | 220 | ||||
| 217 | def __gt__(self, other): | 221 | def __gt__(self, other): | ||
| 218 | return self.get_sum() > other.get_sum() | 222 | return self.get_sum() > other.get_sum() | ||
| 219 | 223 | ||||
| 220 | def __lt__(self, other): | 224 | def __lt__(self, other): | ||
| 221 | return self.get_sum() < other.get_sum() | 225 | return self.get_sum() < other.get_sum() | ||
| 222 | 226 | ||||
| 223 | 227 | ||||
| 224 | class ГоспожатаПоХимия: | 228 | class ГоспожатаПоХимия: | ||
| 225 | def __init__(self): | 229 | def __init__(self): | ||
| 226 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 230 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 227 | self.timer = {} | 231 | self.timer = {} | ||
| 228 | 232 | ||||
| 229 | def apply_effect(self, target, effect): | 233 | def apply_effect(self, target, effect): | ||
| 230 | # creating a revrse function to reverse the changes after the effect expires | 234 | # creating a revrse function to reverse the changes after the effect expires | ||
| 231 | old_attributes = vars(target).copy() | 235 | old_attributes = vars(target).copy() | ||
| 232 | effect(target) | 236 | effect(target) | ||
| 233 | to_reverse = {} | 237 | to_reverse = {} | ||
| 234 | for key, value in old_attributes.items(): | 238 | for key, value in old_attributes.items(): | ||
| 235 | new_value = getattr(target, key) | 239 | new_value = getattr(target, key) | ||
| 236 | if new_value != value: | 240 | if new_value != value: | ||
| 237 | to_reverse[key] = new_value - value | 241 | to_reverse[key] = new_value - value | ||
| 238 | 242 | ||||
| 239 | def reverse_effect(): | 243 | def reverse_effect(): | ||
| 240 | for key, value in to_reverse.items(): | 244 | for key, value in to_reverse.items(): | ||
| 241 | new_value = getattr(target, key) | 245 | new_value = getattr(target, key) | ||
| 242 | setattr(target, key, new_value - value) | 246 | setattr(target, key, new_value - value) | ||
| 243 | 247 | ||||
| 244 | return reverse_effect | 248 | return reverse_effect | ||
| 245 | 249 | ||||
| 246 | def apply(self, target, potion): | 250 | def apply(self, target, potion): | ||
| 247 | if potion.is_used(): | 251 | if potion.is_used(): | ||
| 248 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 252 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 249 | 253 | ||||
| 250 | if potion.is_component: # this is probably useless, but just to be sure | 254 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 251 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 255 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| t | 252 | t | 256 | ordered_dir = sorted(dir(potion), key=calculate_molecular_mass, reverse=True) | |
| 253 | for key in dir(potion): | 257 | for key in ordered_dir: | ||
| 254 | value = getattr(potion, key) | 258 | value = getattr(potion, key) | ||
| 255 | if type(value) is PotionEffect and not value.used: | 259 | if type(value) is PotionEffect and not value.used: | ||
| 256 | reverse_func = self.apply_effect(target, value) | 260 | reverse_func = self.apply_effect(target, value) | ||
| 257 | self.timer[reverse_func] = potion.duration | 261 | self.timer[reverse_func] = potion.duration | ||
| 258 | 262 | ||||
| 259 | def tick(self): | 263 | def tick(self): | ||
| 260 | to_iterate = self.timer.copy().items() | 264 | to_iterate = self.timer.copy().items() | ||
| 261 | for key, value in to_iterate: | 265 | for key, value in to_iterate: | ||
| 262 | self.timer[key] -= 1 | 266 | self.timer[key] -= 1 | ||
| 263 | if value <= 1: | 267 | if value <= 1: | ||
| 264 | key() | 268 | key() | ||
| 265 | self.timer.pop(key) | 269 | self.timer.pop(key) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| n | 3 | USED_INDICATOR_END = "_used" | n | ||
| 4 | TIMES_INDICATOR_END = "_times" | ||||
| 5 | EFFECT_ERROR_TEXT = "Effect is depleted." | 3 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 6 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 4 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 7 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 5 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 8 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 6 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 9 | 7 | ||||
| 10 | 8 | ||||
| 11 | def fake_func(target): | 9 | def fake_func(target): | ||
| 12 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 10 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 13 | 11 | ||||
| 14 | 12 | ||||
| 15 | def custom_round(value): | 13 | def custom_round(value): | ||
| 16 | result = round(value) | 14 | result = round(value) | ||
| 17 | if cmath.isclose( | 15 | if cmath.isclose( | ||
| 18 | result - value, 0.5 | 16 | result - value, 0.5 | ||
| 19 | ): # the edge case where x.5 should be rounded down | 17 | ): # the edge case where x.5 should be rounded down | ||
| 20 | result -= 1 | 18 | result -= 1 | ||
| 21 | return result | 19 | return result | ||
| 22 | 20 | ||||
| 23 | 21 | ||||
| 24 | class PotionEffect: | 22 | class PotionEffect: | ||
| 25 | def __init__(self, func): | 23 | def __init__(self, func): | ||
| 26 | self.func = func | 24 | self.func = func | ||
| 27 | self.times = 1 | 25 | self.times = 1 | ||
| 28 | self.used = False | 26 | self.used = False | ||
| 29 | 27 | ||||
| 30 | def __iadd__(self, other): | 28 | def __iadd__(self, other): | ||
| 31 | self.times += other.times | 29 | self.times += other.times | ||
| 32 | return self | 30 | return self | ||
| 33 | 31 | ||||
| 34 | def __isub__(self, other): | 32 | def __isub__(self, other): | ||
| 35 | self.times -= other.times | 33 | self.times -= other.times | ||
| 36 | return self | 34 | return self | ||
| 37 | 35 | ||||
| 38 | def __imul__(self, other): | 36 | def __imul__(self, other): | ||
| 39 | self.times = custom_round(self.times * other) | 37 | self.times = custom_round(self.times * other) | ||
| 40 | return self | 38 | return self | ||
| 41 | 39 | ||||
| 42 | def __itruediv__(self, other): | 40 | def __itruediv__(self, other): | ||
| 43 | self.times = custom_round(self.times / other) | 41 | self.times = custom_round(self.times / other) | ||
| 44 | return self | 42 | return self | ||
| 45 | 43 | ||||
| 46 | def copy(self): | 44 | def copy(self): | ||
| 47 | result = PotionEffect(self.func) | 45 | result = PotionEffect(self.func) | ||
| 48 | result.times = self.times | 46 | result.times = self.times | ||
| 49 | result.used = self.used | 47 | result.used = self.used | ||
| 50 | return result | 48 | return result | ||
| 51 | 49 | ||||
| 52 | def __call__(self, target): | 50 | def __call__(self, target): | ||
| 53 | if self.used: | 51 | if self.used: | ||
| 54 | raise TypeError(EFFECT_ERROR_TEXT) | 52 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 55 | for _ in range(self.times): | 53 | for _ in range(self.times): | ||
| 56 | self.func(target) | 54 | self.func(target) | ||
| 57 | self.used = True | 55 | self.used = True | ||
| 58 | 56 | ||||
| 59 | 57 | ||||
| 60 | class meta(type): | 58 | class meta(type): | ||
| 61 | def __new__(cls, name, bases, attr_dict): | 59 | def __new__(cls, name, bases, attr_dict): | ||
| 62 | attr_dict["is_component"] = False | 60 | attr_dict["is_component"] = False | ||
| 63 | return type.__new__(cls, name, bases, attr_dict) | 61 | return type.__new__(cls, name, bases, attr_dict) | ||
| 64 | 62 | ||||
| 65 | 63 | ||||
| 66 | class Potion(metaclass=meta): | 64 | class Potion(metaclass=meta): | ||
| 67 | def __init__(self, effects, duration): | 65 | def __init__(self, effects, duration): | ||
| 68 | for key, value in effects.items(): | 66 | for key, value in effects.items(): | ||
| 69 | setattr(self, key, PotionEffect(value)) | 67 | setattr(self, key, PotionEffect(value)) | ||
| 70 | self.duration = duration | 68 | self.duration = duration | ||
| 71 | 69 | ||||
| 72 | def is_used(self): | 70 | def is_used(self): | ||
| 73 | for key in dir(self): | 71 | for key in dir(self): | ||
| 74 | value = getattr(self, key) | 72 | value = getattr(self, key) | ||
| 75 | if type(value) is PotionEffect and not value.used: | 73 | if type(value) is PotionEffect and not value.used: | ||
| 76 | return False | 74 | return False | ||
| 77 | return True | 75 | return True | ||
| 78 | 76 | ||||
| 79 | def __getattribute__(self, name): | 77 | def __getattribute__(self, name): | ||
| 80 | result = object.__getattribute__(self, name) | 78 | result = object.__getattribute__(self, name) | ||
| 81 | if ( | 79 | if ( | ||
| 82 | object.__getattribute__(self, "is_component") | 80 | object.__getattribute__(self, "is_component") | ||
| 83 | and type(result) is PotionEffect | 81 | and type(result) is PotionEffect | ||
| 84 | ): | 82 | ): | ||
| 85 | fake_result = PotionEffect(fake_func) | 83 | fake_result = PotionEffect(fake_func) | ||
| 86 | fake_result.times = result.times | 84 | fake_result.times = result.times | ||
| 87 | fake_result.used = result.used | 85 | fake_result.used = result.used | ||
| 88 | return fake_result | 86 | return fake_result | ||
| 89 | 87 | ||||
| 90 | return result | 88 | return result | ||
| 91 | 89 | ||||
| 92 | def __add__(self, other): | 90 | def __add__(self, other): | ||
| 93 | if self.is_component or other.is_component: | 91 | if self.is_component or other.is_component: | ||
| 94 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 92 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 95 | if self.is_used() or other.is_used(): | 93 | if self.is_used() or other.is_used(): | ||
| 96 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 94 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 97 | 95 | ||||
| 98 | # transfering the methods from the object on the left side to dict, | 96 | # transfering the methods from the object on the left side to dict, | ||
| 99 | # and if there are matching methods in the right side, we increace the intensity | 97 | # and if there are matching methods in the right side, we increace the intensity | ||
| 100 | result_dict = {} | 98 | result_dict = {} | ||
| 101 | for key in dir(self): | 99 | for key in dir(self): | ||
| 102 | value = getattr(self, key) | 100 | value = getattr(self, key) | ||
| 103 | if type(value) is PotionEffect and not value.used: | 101 | if type(value) is PotionEffect and not value.used: | ||
| 104 | result_dict[key] = value.copy() | 102 | result_dict[key] = value.copy() | ||
| 105 | if key in dir(other): | 103 | if key in dir(other): | ||
| 106 | other_value = getattr(other, key) | 104 | other_value = getattr(other, key) | ||
| 107 | if not other_value.used: | 105 | if not other_value.used: | ||
| 108 | result_dict[key] += other_value | 106 | result_dict[key] += other_value | ||
| 109 | 107 | ||||
| 110 | # transfering the methods that are only in the right side to the dict | 108 | # transfering the methods that are only in the right side to the dict | ||
| 111 | for key in dir(other): | 109 | for key in dir(other): | ||
| 112 | value = getattr(other, key) | 110 | value = getattr(other, key) | ||
| 113 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | 111 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | ||
| 114 | result_dict[key] = getattr(other, key).copy() | 112 | result_dict[key] = getattr(other, key).copy() | ||
| 115 | 113 | ||||
| 116 | result_dict["duration"] = max(self.duration, other.duration) | 114 | result_dict["duration"] = max(self.duration, other.duration) | ||
| 117 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 115 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 118 | self.is_component = True | 116 | self.is_component = True | ||
| 119 | other.is_component = True | 117 | other.is_component = True | ||
| 120 | 118 | ||||
| 121 | return result | 119 | return result | ||
| 122 | 120 | ||||
| 123 | def __mul__(self, other): | 121 | def __mul__(self, other): | ||
| 124 | if self.is_component: | 122 | if self.is_component: | ||
| 125 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 123 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 126 | if self.is_used(): | 124 | if self.is_used(): | ||
| 127 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 125 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 128 | result_dict = {} | 126 | result_dict = {} | ||
| 129 | # transfering the methods from the object to the dict and multiply them with the number | 127 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 130 | for key in dir(self): | 128 | for key in dir(self): | ||
| 131 | value = getattr(self, key) | 129 | value = getattr(self, key) | ||
| 132 | if type(value) is PotionEffect: | 130 | if type(value) is PotionEffect: | ||
| 133 | result_dict[key] = value.copy() | 131 | result_dict[key] = value.copy() | ||
| 134 | result_dict[key] *= other | 132 | result_dict[key] *= other | ||
| 135 | 133 | ||||
| 136 | result_dict["duration"] = self.duration | 134 | result_dict["duration"] = self.duration | ||
| 137 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 135 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 138 | self.is_component = True | 136 | self.is_component = True | ||
| 139 | 137 | ||||
| 140 | return result | 138 | return result | ||
| 141 | 139 | ||||
| 142 | def __sub__(self, other): | 140 | def __sub__(self, other): | ||
| 143 | if self.is_component or other.is_component: | 141 | if self.is_component or other.is_component: | ||
| 144 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 142 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 145 | if self.is_used() or other.is_used(): | 143 | if self.is_used() or other.is_used(): | ||
| 146 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 144 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 147 | result_dict = {} | 145 | result_dict = {} | ||
| 148 | # validation that the substitution is valid | 146 | # validation that the substitution is valid | ||
| 149 | for key in dir(other): | 147 | for key in dir(other): | ||
| 150 | value = getattr(other, key) | 148 | value = getattr(other, key) | ||
| 151 | if type(value) is PotionEffect and not key in dir(self): | 149 | if type(value) is PotionEffect and not key in dir(self): | ||
| 152 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 150 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 153 | # transfering the methods from the object on the left side to dict, | 151 | # transfering the methods from the object on the left side to dict, | ||
| 154 | # and if there are matching methods in the right side, we subtract their intensity | 152 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 155 | for key in dir(self): | 153 | for key in dir(self): | ||
| 156 | value = getattr(self, key) | 154 | value = getattr(self, key) | ||
| 157 | if type(value) is PotionEffect: | 155 | if type(value) is PotionEffect: | ||
| 158 | result_dict[key] = value.copy() | 156 | result_dict[key] = value.copy() | ||
| 159 | if key in dir(other): | 157 | if key in dir(other): | ||
| 160 | other_value = getattr(other, key) | 158 | other_value = getattr(other, key) | ||
| 161 | if not other_value.used: | 159 | if not other_value.used: | ||
| 162 | result_dict[key] -= getattr(other, key) | 160 | result_dict[key] -= getattr(other, key) | ||
| 163 | if result_dict[key].times <= 0: | 161 | if result_dict[key].times <= 0: | ||
| 164 | result_dict.pop(key) | 162 | result_dict.pop(key) | ||
| 165 | 163 | ||||
| 166 | result_dict["duration"] = self.duration | 164 | result_dict["duration"] = self.duration | ||
| 167 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 165 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 168 | self.is_component = True | 166 | self.is_component = True | ||
| 169 | other.is_component = True | 167 | other.is_component = True | ||
| 170 | return result | 168 | return result | ||
| 171 | 169 | ||||
| 172 | def __truediv__(self, other): | 170 | def __truediv__(self, other): | ||
| 173 | if self.is_component: | 171 | if self.is_component: | ||
| 174 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 172 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 175 | if self.is_used(): | 173 | if self.is_used(): | ||
| 176 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 174 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 177 | # spliting the object into equal parts, where the intensity is devided by the number | 175 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 178 | result_list = [] | 176 | result_list = [] | ||
| 179 | for _ in range(other): | 177 | for _ in range(other): | ||
| 180 | result_dict = {} | 178 | result_dict = {} | ||
| 181 | for key in dir(self): | 179 | for key in dir(self): | ||
| 182 | value = getattr(self, key) | 180 | value = getattr(self, key) | ||
| 183 | if type(value) is PotionEffect: | 181 | if type(value) is PotionEffect: | ||
| 184 | result_dict[key] = value.copy() | 182 | result_dict[key] = value.copy() | ||
| 185 | result_dict[key] /= other | 183 | result_dict[key] /= other | ||
| 186 | 184 | ||||
| 187 | result_dict["duration"] = self.duration | 185 | result_dict["duration"] = self.duration | ||
| 188 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 186 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 189 | result_list.append(result) | 187 | result_list.append(result) | ||
| 190 | 188 | ||||
| 191 | self.is_component = True | 189 | self.is_component = True | ||
| 192 | return tuple(result_list) | 190 | return tuple(result_list) | ||
| 193 | 191 | ||||
| 194 | def __eq__(self, other): | 192 | def __eq__(self, other): | ||
| 195 | for key in dir(self): | 193 | for key in dir(self): | ||
| 196 | value = getattr(self, key) | 194 | value = getattr(self, key) | ||
| 197 | if type(value) is PotionEffect: | 195 | if type(value) is PotionEffect: | ||
| 198 | if not key in dir(other): | 196 | if not key in dir(other): | ||
| 199 | return False | 197 | return False | ||
| 200 | other_value = getattr(other, key) | 198 | other_value = getattr(other, key) | ||
| 201 | if value.times != other_value.times or value.used != other_value.used: | 199 | if value.times != other_value.times or value.used != other_value.used: | ||
| 202 | return False | 200 | return False | ||
| 203 | 201 | ||||
| 204 | for key in dir(other): | 202 | for key in dir(other): | ||
| 205 | value = getattr(other, key) | 203 | value = getattr(other, key) | ||
| 206 | if type(value) is PotionEffect and not key in dir(self): | 204 | if type(value) is PotionEffect and not key in dir(self): | ||
| 207 | return False | 205 | return False | ||
| 208 | 206 | ||||
| 209 | return True | 207 | return True | ||
| 210 | 208 | ||||
| 211 | def get_sum(self): | 209 | def get_sum(self): | ||
| 212 | result = 0 | 210 | result = 0 | ||
| 213 | for key in dir(self): | 211 | for key in dir(self): | ||
| 214 | value = getattr(self, key) | 212 | value = getattr(self, key) | ||
| 215 | if type(value) is PotionEffect: | 213 | if type(value) is PotionEffect: | ||
| 216 | result += value.times | 214 | result += value.times | ||
| 217 | return result | 215 | return result | ||
| 218 | 216 | ||||
| 219 | def __gt__(self, other): | 217 | def __gt__(self, other): | ||
| 220 | return self.get_sum() > other.get_sum() | 218 | return self.get_sum() > other.get_sum() | ||
| 221 | 219 | ||||
| 222 | def __lt__(self, other): | 220 | def __lt__(self, other): | ||
| 223 | return self.get_sum() < other.get_sum() | 221 | return self.get_sum() < other.get_sum() | ||
| 224 | 222 | ||||
| 225 | 223 | ||||
| 226 | class ГоспожатаПоХимия: | 224 | class ГоспожатаПоХимия: | ||
| 227 | def __init__(self): | 225 | def __init__(self): | ||
| 228 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 226 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 229 | self.timer = {} | 227 | self.timer = {} | ||
| 230 | 228 | ||||
| 231 | def apply_effect(self, target, effect): | 229 | def apply_effect(self, target, effect): | ||
| 232 | # creating a revrse function to reverse the changes after the effect expires | 230 | # creating a revrse function to reverse the changes after the effect expires | ||
| 233 | old_attributes = vars(target).copy() | 231 | old_attributes = vars(target).copy() | ||
| 234 | effect(target) | 232 | effect(target) | ||
| 235 | to_reverse = {} | 233 | to_reverse = {} | ||
| 236 | for key, value in old_attributes.items(): | 234 | for key, value in old_attributes.items(): | ||
| 237 | new_value = getattr(target, key) | 235 | new_value = getattr(target, key) | ||
| 238 | if new_value != value: | 236 | if new_value != value: | ||
| 239 | to_reverse[key] = new_value - value | 237 | to_reverse[key] = new_value - value | ||
| 240 | 238 | ||||
| 241 | def reverse_effect(): | 239 | def reverse_effect(): | ||
| 242 | for key, value in to_reverse.items(): | 240 | for key, value in to_reverse.items(): | ||
| 243 | new_value = getattr(target, key) | 241 | new_value = getattr(target, key) | ||
| 244 | setattr(target, key, new_value - value) | 242 | setattr(target, key, new_value - value) | ||
| 245 | 243 | ||||
| 246 | return reverse_effect | 244 | return reverse_effect | ||
| 247 | 245 | ||||
| 248 | def apply(self, target, potion): | 246 | def apply(self, target, potion): | ||
| 249 | if potion.is_used(): | 247 | if potion.is_used(): | ||
| 250 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 248 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 251 | 249 | ||||
| 252 | if potion.is_component: # this is probably useless, but just to be sure | 250 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 253 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 251 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 254 | 252 | ||||
| 255 | for key in dir(potion): | 253 | for key in dir(potion): | ||
| 256 | value = getattr(potion, key) | 254 | value = getattr(potion, key) | ||
| 257 | if type(value) is PotionEffect and not value.used: | 255 | if type(value) is PotionEffect and not value.used: | ||
| 258 | reverse_func = self.apply_effect(target, value) | 256 | reverse_func = self.apply_effect(target, value) | ||
| 259 | self.timer[reverse_func] = potion.duration | 257 | self.timer[reverse_func] = potion.duration | ||
| 260 | 258 | ||||
| 261 | def tick(self): | 259 | def tick(self): | ||
| 262 | to_iterate = self.timer.copy().items() | 260 | to_iterate = self.timer.copy().items() | ||
| 263 | for key, value in to_iterate: | 261 | for key, value in to_iterate: | ||
| n | n | 262 | self.timer[key] -= 1 | ||
| 264 | if value == 0: | 263 | if value <= 1: | ||
| 265 | key() | 264 | key() | ||
| 266 | self.timer.pop(key) | 265 | self.timer.pop(key) | ||
| t | 267 | else: | t | ||
| 268 | self.timer[key] -= 1 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import cmath | f | 1 | import cmath |
| 2 | 2 | ||||
| 3 | USED_INDICATOR_END = "_used" | 3 | USED_INDICATOR_END = "_used" | ||
| 4 | TIMES_INDICATOR_END = "_times" | 4 | TIMES_INDICATOR_END = "_times" | ||
| 5 | EFFECT_ERROR_TEXT = "Effect is depleted." | 5 | EFFECT_ERROR_TEXT = "Effect is depleted." | ||
| 6 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | 6 | POTION_IS_COMPONENT_ERROR_TEXT = "Potion is now part of something bigger than itself." | ||
| 7 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | 7 | INVALID_SUBSTITUTION_ERROR_TEXT = "Invalid substitution" | ||
| 8 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | 8 | POTION_IS_USED_ERROR_TEXT = "Potion is depleted." | ||
| 9 | 9 | ||||
| 10 | 10 | ||||
| 11 | def fake_func(target): | 11 | def fake_func(target): | ||
| 12 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 12 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 13 | 13 | ||||
| 14 | 14 | ||||
| 15 | def custom_round(value): | 15 | def custom_round(value): | ||
| 16 | result = round(value) | 16 | result = round(value) | ||
| 17 | if cmath.isclose( | 17 | if cmath.isclose( | ||
| 18 | result - value, 0.5 | 18 | result - value, 0.5 | ||
| 19 | ): # the edge case where x.5 should be rounded down | 19 | ): # the edge case where x.5 should be rounded down | ||
| 20 | result -= 1 | 20 | result -= 1 | ||
| 21 | return result | 21 | return result | ||
| 22 | 22 | ||||
| 23 | 23 | ||||
| 24 | class PotionEffect: | 24 | class PotionEffect: | ||
| 25 | def __init__(self, func): | 25 | def __init__(self, func): | ||
| 26 | self.func = func | 26 | self.func = func | ||
| 27 | self.times = 1 | 27 | self.times = 1 | ||
| 28 | self.used = False | 28 | self.used = False | ||
| 29 | 29 | ||||
| 30 | def __iadd__(self, other): | 30 | def __iadd__(self, other): | ||
| 31 | self.times += other.times | 31 | self.times += other.times | ||
| 32 | return self | 32 | return self | ||
| 33 | 33 | ||||
| 34 | def __isub__(self, other): | 34 | def __isub__(self, other): | ||
| 35 | self.times -= other.times | 35 | self.times -= other.times | ||
| 36 | return self | 36 | return self | ||
| 37 | 37 | ||||
| 38 | def __imul__(self, other): | 38 | def __imul__(self, other): | ||
| 39 | self.times = custom_round(self.times * other) | 39 | self.times = custom_round(self.times * other) | ||
| 40 | return self | 40 | return self | ||
| 41 | 41 | ||||
| 42 | def __itruediv__(self, other): | 42 | def __itruediv__(self, other): | ||
| 43 | self.times = custom_round(self.times / other) | 43 | self.times = custom_round(self.times / other) | ||
| 44 | return self | 44 | return self | ||
| 45 | 45 | ||||
| 46 | def copy(self): | 46 | def copy(self): | ||
| 47 | result = PotionEffect(self.func) | 47 | result = PotionEffect(self.func) | ||
| 48 | result.times = self.times | 48 | result.times = self.times | ||
| 49 | result.used = self.used | 49 | result.used = self.used | ||
| 50 | return result | 50 | return result | ||
| 51 | 51 | ||||
| 52 | def __call__(self, target): | 52 | def __call__(self, target): | ||
| 53 | if self.used: | 53 | if self.used: | ||
| 54 | raise TypeError(EFFECT_ERROR_TEXT) | 54 | raise TypeError(EFFECT_ERROR_TEXT) | ||
| 55 | for _ in range(self.times): | 55 | for _ in range(self.times): | ||
| 56 | self.func(target) | 56 | self.func(target) | ||
| 57 | self.used = True | 57 | self.used = True | ||
| 58 | 58 | ||||
| 59 | 59 | ||||
| 60 | class meta(type): | 60 | class meta(type): | ||
| 61 | def __new__(cls, name, bases, attr_dict): | 61 | def __new__(cls, name, bases, attr_dict): | ||
| 62 | attr_dict["is_component"] = False | 62 | attr_dict["is_component"] = False | ||
| 63 | return type.__new__(cls, name, bases, attr_dict) | 63 | return type.__new__(cls, name, bases, attr_dict) | ||
| 64 | 64 | ||||
| 65 | 65 | ||||
| 66 | class Potion(metaclass=meta): | 66 | class Potion(metaclass=meta): | ||
| 67 | def __init__(self, effects, duration): | 67 | def __init__(self, effects, duration): | ||
| 68 | for key, value in effects.items(): | 68 | for key, value in effects.items(): | ||
| 69 | setattr(self, key, PotionEffect(value)) | 69 | setattr(self, key, PotionEffect(value)) | ||
| 70 | self.duration = duration | 70 | self.duration = duration | ||
| 71 | 71 | ||||
| 72 | def is_used(self): | 72 | def is_used(self): | ||
| 73 | for key in dir(self): | 73 | for key in dir(self): | ||
| 74 | value = getattr(self, key) | 74 | value = getattr(self, key) | ||
| 75 | if type(value) is PotionEffect and not value.used: | 75 | if type(value) is PotionEffect and not value.used: | ||
| 76 | return False | 76 | return False | ||
| 77 | return True | 77 | return True | ||
| 78 | 78 | ||||
| 79 | def __getattribute__(self, name): | 79 | def __getattribute__(self, name): | ||
| 80 | result = object.__getattribute__(self, name) | 80 | result = object.__getattribute__(self, name) | ||
| 81 | if ( | 81 | if ( | ||
| 82 | object.__getattribute__(self, "is_component") | 82 | object.__getattribute__(self, "is_component") | ||
| 83 | and type(result) is PotionEffect | 83 | and type(result) is PotionEffect | ||
| 84 | ): | 84 | ): | ||
| 85 | fake_result = PotionEffect(fake_func) | 85 | fake_result = PotionEffect(fake_func) | ||
| 86 | fake_result.times = result.times | 86 | fake_result.times = result.times | ||
| 87 | fake_result.used = result.used | 87 | fake_result.used = result.used | ||
| 88 | return fake_result | 88 | return fake_result | ||
| 89 | 89 | ||||
| 90 | return result | 90 | return result | ||
| 91 | 91 | ||||
| 92 | def __add__(self, other): | 92 | def __add__(self, other): | ||
| 93 | if self.is_component or other.is_component: | 93 | if self.is_component or other.is_component: | ||
| 94 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 94 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| n | n | 95 | if self.is_used() or other.is_used(): | ||
| 96 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 95 | 97 | ||||
| 96 | # transfering the methods from the object on the left side to dict, | 98 | # transfering the methods from the object on the left side to dict, | ||
| 97 | # and if there are matching methods in the right side, we increace the intensity | 99 | # and if there are matching methods in the right side, we increace the intensity | ||
| 98 | result_dict = {} | 100 | result_dict = {} | ||
| 99 | for key in dir(self): | 101 | for key in dir(self): | ||
| 100 | value = getattr(self, key) | 102 | value = getattr(self, key) | ||
| 101 | if type(value) is PotionEffect and not value.used: | 103 | if type(value) is PotionEffect and not value.used: | ||
| 102 | result_dict[key] = value.copy() | 104 | result_dict[key] = value.copy() | ||
| 103 | if key in dir(other): | 105 | if key in dir(other): | ||
| 104 | other_value = getattr(other, key) | 106 | other_value = getattr(other, key) | ||
| 105 | if not other_value.used: | 107 | if not other_value.used: | ||
| 106 | result_dict[key] += other_value | 108 | result_dict[key] += other_value | ||
| 107 | 109 | ||||
| 108 | # transfering the methods that are only in the right side to the dict | 110 | # transfering the methods that are only in the right side to the dict | ||
| 109 | for key in dir(other): | 111 | for key in dir(other): | ||
| 110 | value = getattr(other, key) | 112 | value = getattr(other, key) | ||
| 111 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | 113 | if type(value) is PotionEffect and not key in dir(self) and not value.used: | ||
| 112 | result_dict[key] = getattr(other, key).copy() | 114 | result_dict[key] = getattr(other, key).copy() | ||
| 113 | 115 | ||||
| 114 | result_dict["duration"] = max(self.duration, other.duration) | 116 | result_dict["duration"] = max(self.duration, other.duration) | ||
| 115 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 117 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 116 | self.is_component = True | 118 | self.is_component = True | ||
| 117 | other.is_component = True | 119 | other.is_component = True | ||
| 118 | 120 | ||||
| 119 | return result | 121 | return result | ||
| 120 | 122 | ||||
| 121 | def __mul__(self, other): | 123 | def __mul__(self, other): | ||
| 122 | if self.is_component: | 124 | if self.is_component: | ||
| 123 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 125 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| n | 124 | n | 126 | if self.is_used(): | |
| 127 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 125 | result_dict = {} | 128 | result_dict = {} | ||
| 126 | # transfering the methods from the object to the dict and multiply them with the number | 129 | # transfering the methods from the object to the dict and multiply them with the number | ||
| 127 | for key in dir(self): | 130 | for key in dir(self): | ||
| 128 | value = getattr(self, key) | 131 | value = getattr(self, key) | ||
| 129 | if type(value) is PotionEffect: | 132 | if type(value) is PotionEffect: | ||
| 130 | result_dict[key] = value.copy() | 133 | result_dict[key] = value.copy() | ||
| 131 | result_dict[key] *= other | 134 | result_dict[key] *= other | ||
| 132 | 135 | ||||
| 133 | result_dict["duration"] = self.duration | 136 | result_dict["duration"] = self.duration | ||
| 134 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 137 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 135 | self.is_component = True | 138 | self.is_component = True | ||
| 136 | 139 | ||||
| 137 | return result | 140 | return result | ||
| 138 | 141 | ||||
| 139 | def __sub__(self, other): | 142 | def __sub__(self, other): | ||
| 140 | if self.is_component or other.is_component: | 143 | if self.is_component or other.is_component: | ||
| 141 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 144 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| n | 142 | n | 145 | if self.is_used() or other.is_used(): | |
| 146 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 143 | result_dict = {} | 147 | result_dict = {} | ||
| 144 | # validation that the substitution is valid | 148 | # validation that the substitution is valid | ||
| 145 | for key in dir(other): | 149 | for key in dir(other): | ||
| 146 | value = getattr(other, key) | 150 | value = getattr(other, key) | ||
| 147 | if type(value) is PotionEffect and not key in dir(self): | 151 | if type(value) is PotionEffect and not key in dir(self): | ||
| 148 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | 152 | raise TypeError(INVALID_SUBSTITUTION_ERROR_TEXT) | ||
| 149 | # transfering the methods from the object on the left side to dict, | 153 | # transfering the methods from the object on the left side to dict, | ||
| 150 | # and if there are matching methods in the right side, we subtract their intensity | 154 | # and if there are matching methods in the right side, we subtract their intensity | ||
| 151 | for key in dir(self): | 155 | for key in dir(self): | ||
| 152 | value = getattr(self, key) | 156 | value = getattr(self, key) | ||
| 153 | if type(value) is PotionEffect: | 157 | if type(value) is PotionEffect: | ||
| 154 | result_dict[key] = value.copy() | 158 | result_dict[key] = value.copy() | ||
| 155 | if key in dir(other): | 159 | if key in dir(other): | ||
| 156 | other_value = getattr(other, key) | 160 | other_value = getattr(other, key) | ||
| 157 | if not other_value.used: | 161 | if not other_value.used: | ||
| 158 | result_dict[key] -= getattr(other, key) | 162 | result_dict[key] -= getattr(other, key) | ||
| 159 | if result_dict[key].times <= 0: | 163 | if result_dict[key].times <= 0: | ||
| 160 | result_dict.pop(key) | 164 | result_dict.pop(key) | ||
| 161 | 165 | ||||
| 162 | result_dict["duration"] = self.duration | 166 | result_dict["duration"] = self.duration | ||
| 163 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 167 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 164 | self.is_component = True | 168 | self.is_component = True | ||
| 165 | other.is_component = True | 169 | other.is_component = True | ||
| 166 | return result | 170 | return result | ||
| 167 | 171 | ||||
| 168 | def __truediv__(self, other): | 172 | def __truediv__(self, other): | ||
| 169 | if self.is_component: | 173 | if self.is_component: | ||
| 170 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 174 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| t | t | 175 | if self.is_used(): | ||
| 176 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||||
| 171 | # spliting the object into equal parts, where the intensity is devided by the number | 177 | # spliting the object into equal parts, where the intensity is devided by the number | ||
| 172 | result_list = [] | 178 | result_list = [] | ||
| 173 | for _ in range(other): | 179 | for _ in range(other): | ||
| 174 | result_dict = {} | 180 | result_dict = {} | ||
| 175 | for key in dir(self): | 181 | for key in dir(self): | ||
| 176 | value = getattr(self, key) | 182 | value = getattr(self, key) | ||
| 177 | if type(value) is PotionEffect: | 183 | if type(value) is PotionEffect: | ||
| 178 | result_dict[key] = value.copy() | 184 | result_dict[key] = value.copy() | ||
| 179 | result_dict[key] /= other | 185 | result_dict[key] /= other | ||
| 180 | 186 | ||||
| 181 | result_dict["duration"] = self.duration | 187 | result_dict["duration"] = self.duration | ||
| 182 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | 188 | result = meta("Potion", (Potion,), result_dict)({}, result_dict["duration"]) | ||
| 183 | result_list.append(result) | 189 | result_list.append(result) | ||
| 184 | 190 | ||||
| 185 | self.is_component = True | 191 | self.is_component = True | ||
| 186 | return tuple(result_list) | 192 | return tuple(result_list) | ||
| 187 | 193 | ||||
| 188 | def __eq__(self, other): | 194 | def __eq__(self, other): | ||
| 189 | for key in dir(self): | 195 | for key in dir(self): | ||
| 190 | value = getattr(self, key) | 196 | value = getattr(self, key) | ||
| 191 | if type(value) is PotionEffect: | 197 | if type(value) is PotionEffect: | ||
| 192 | if not key in dir(other): | 198 | if not key in dir(other): | ||
| 193 | return False | 199 | return False | ||
| 194 | other_value = getattr(other, key) | 200 | other_value = getattr(other, key) | ||
| 195 | if value.times != other_value.times or value.used != other_value.used: | 201 | if value.times != other_value.times or value.used != other_value.used: | ||
| 196 | return False | 202 | return False | ||
| 197 | 203 | ||||
| 198 | for key in dir(other): | 204 | for key in dir(other): | ||
| 199 | value = getattr(other, key) | 205 | value = getattr(other, key) | ||
| 200 | if type(value) is PotionEffect and not key in dir(self): | 206 | if type(value) is PotionEffect and not key in dir(self): | ||
| 201 | return False | 207 | return False | ||
| 202 | 208 | ||||
| 203 | return True | 209 | return True | ||
| 204 | 210 | ||||
| 205 | def get_sum(self): | 211 | def get_sum(self): | ||
| 206 | result = 0 | 212 | result = 0 | ||
| 207 | for key in dir(self): | 213 | for key in dir(self): | ||
| 208 | value = getattr(self, key) | 214 | value = getattr(self, key) | ||
| 209 | if type(value) is PotionEffect: | 215 | if type(value) is PotionEffect: | ||
| 210 | result += value.times | 216 | result += value.times | ||
| 211 | return result | 217 | return result | ||
| 212 | 218 | ||||
| 213 | def __gt__(self, other): | 219 | def __gt__(self, other): | ||
| 214 | return self.get_sum() > other.get_sum() | 220 | return self.get_sum() > other.get_sum() | ||
| 215 | 221 | ||||
| 216 | def __lt__(self, other): | 222 | def __lt__(self, other): | ||
| 217 | return self.get_sum() < other.get_sum() | 223 | return self.get_sum() < other.get_sum() | ||
| 218 | 224 | ||||
| 219 | 225 | ||||
| 220 | class ГоспожатаПоХимия: | 226 | class ГоспожатаПоХимия: | ||
| 221 | def __init__(self): | 227 | def __init__(self): | ||
| 222 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | 228 | # dictionary to store the reverse effectss of potions as a key, and when to be executed as values | ||
| 223 | self.timer = {} | 229 | self.timer = {} | ||
| 224 | 230 | ||||
| 225 | def apply_effect(self, target, effect): | 231 | def apply_effect(self, target, effect): | ||
| 226 | # creating a revrse function to reverse the changes after the effect expires | 232 | # creating a revrse function to reverse the changes after the effect expires | ||
| 227 | old_attributes = vars(target).copy() | 233 | old_attributes = vars(target).copy() | ||
| 228 | effect(target) | 234 | effect(target) | ||
| 229 | to_reverse = {} | 235 | to_reverse = {} | ||
| 230 | for key, value in old_attributes.items(): | 236 | for key, value in old_attributes.items(): | ||
| 231 | new_value = getattr(target, key) | 237 | new_value = getattr(target, key) | ||
| 232 | if new_value != value: | 238 | if new_value != value: | ||
| 233 | to_reverse[key] = new_value - value | 239 | to_reverse[key] = new_value - value | ||
| 234 | 240 | ||||
| 235 | def reverse_effect(): | 241 | def reverse_effect(): | ||
| 236 | for key, value in to_reverse.items(): | 242 | for key, value in to_reverse.items(): | ||
| 237 | new_value = getattr(target, key) | 243 | new_value = getattr(target, key) | ||
| 238 | setattr(target, key, new_value - value) | 244 | setattr(target, key, new_value - value) | ||
| 239 | 245 | ||||
| 240 | return reverse_effect | 246 | return reverse_effect | ||
| 241 | 247 | ||||
| 242 | def apply(self, target, potion): | 248 | def apply(self, target, potion): | ||
| 243 | if potion.is_used(): | 249 | if potion.is_used(): | ||
| 244 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | 250 | raise TypeError(POTION_IS_USED_ERROR_TEXT) | ||
| 245 | 251 | ||||
| 246 | if potion.is_component: # this is probably useless, but just to be sure | 252 | if potion.is_component: # this is probably useless, but just to be sure | ||
| 247 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | 253 | raise TypeError(POTION_IS_COMPONENT_ERROR_TEXT) | ||
| 248 | 254 | ||||
| 249 | for key in dir(potion): | 255 | for key in dir(potion): | ||
| 250 | value = getattr(potion, key) | 256 | value = getattr(potion, key) | ||
| 251 | if type(value) is PotionEffect and not value.used: | 257 | if type(value) is PotionEffect and not value.used: | ||
| 252 | reverse_func = self.apply_effect(target, value) | 258 | reverse_func = self.apply_effect(target, value) | ||
| 253 | self.timer[reverse_func] = potion.duration | 259 | self.timer[reverse_func] = potion.duration | ||
| 254 | 260 | ||||
| 255 | def tick(self): | 261 | def tick(self): | ||
| 256 | to_iterate = self.timer.copy().items() | 262 | to_iterate = self.timer.copy().items() | ||
| 257 | for key, value in to_iterate: | 263 | for key, value in to_iterate: | ||
| 258 | if value == 0: | 264 | if value == 0: | ||
| 259 | key() | 265 | key() | ||
| 260 | self.timer.pop(key) | 266 | self.timer.pop(key) | ||
| 261 | else: | 267 | else: | ||
| 262 | self.timer[key] -= 1 | 268 | self.timer[key] -= 1 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||