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 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|