Домашни > Работилница за отвари! > Решения > Решението на Цветомир Гълъбов

Резултати
10 точки от тестове
0 точки от учител

10 точки общо

19 успешни теста
1 неуспешни теста
Код

  1import math
  2import copy
  3from collections import OrderedDict
  4
  5
  6class Potion:
  7    def __init__(self, effects, duration):
  8        self.effects = {effect: [func, 1] for effect, func in effects.items()}
  9        self.duration = duration
 10        self.used_in_reation = False
 11        self.depleted = False
 12
 13    def __getattr__(self, attribute_name):
 14        if (
 15            attribute_name in self.effects
 16            and self.effects[attribute_name][0] is not None
 17        ):
 18            return lambda target: self._apply_effect(
 19                target, attribute_name, self.effects[attribute_name][0]
 20            )
 21        elif self.depleted == True:
 22            raise TypeError("Potion is depleted.")
 23        elif attribute_name in self.effects and self.effects[attribute_name][0] is None:
 24            raise TypeError("Effect is depleted.")
 25        else:
 26            raise AttributeError(f"Object doesn't have attribute {attribute_name}.")
 27
 28    def _potion_validation_check(func):
 29        def wrapper(*args, **kwargs):
 30            if args[0].used_in_reation == True:
 31                raise TypeError("Potion is now part of something bigger than itself.")
 32            elif isinstance(args[1], Potion) and args[1].used_in_reation == True:
 33                raise TypeError("Potion is now part of something bigger than itself.")
 34            elif args[0].depleted == True:
 35                raise TypeError("Potion is depleted.")
 36            elif isinstance(args[1], Potion) and args[1].depleted == True:
 37                raise TypeError("Potion is depleted.")
 38            return func(*args, **kwargs)
 39
 40        return wrapper
 41
 42    @_potion_validation_check
 43    def _apply_effect(self, target, effect_name, effect):
 44        for _ in range(self.effects[effect_name][1]):
 45            effect(target)
 46        self.effects[effect_name][0] = None
 47        self.effects[effect_name][1] = 0
 48        # we mark the used effect function as None
 49        # and intesity as none
 50
 51    @_potion_validation_check
 52    def __add__(self, other):
 53        if not isinstance(other, Potion):
 54            raise TypeError("Addition is only supported between Potion instances.")
 55        combined_effects = self.effects
 56
 57        for effect_name, effect_intensity in other.effects.items():
 58            if effect_name not in combined_effects:
 59                combined_effects[effect_name] = effect_intensity
 60            else:
 61                if (
 62                    combined_effects[effect_name][0] is None
 63                    and effect_intensity[0] is None
 64                ):
 65                    combined_effects[effect_name] = [
 66                        None,
 67                        combined_effects[effect_name][1] + effect_intensity[1],
 68                    ]
 69                elif (
 70                    combined_effects[effect_name][0] is not None
 71                    and effect_intensity[0] is None
 72                ):
 73                    pass
 74                elif (
 75                    combined_effects[effect_name][0] is None
 76                    and effect_intensity[0] is not None
 77                ):
 78                    combined_effects[effect_name] = [
 79                        effect_intensity[0],
 80                        effect_intensity[1],
 81                    ]
 82                else:
 83                    combined_effects[effect_name][1] = (
 84                        combined_effects[effect_name][1] + effect_intensity[1]
 85                    )
 86
 87        combined_duration = max(self.duration, other.duration)
 88        combined_potion = Potion({}, combined_duration)
 89        combined_potion.effects = combined_effects
 90        self.used_in_reation = True
 91        other.used_in_reation = True
 92        return combined_potion
 93
 94    @_potion_validation_check
 95    def multiplication(self, factor):
 96        effects_multiplied_intesities = {
 97            effect: [func_intensity[0], func_intensity[1] * factor]
 98            for effect, func_intensity in self.effects.items()
 99        }
100        multiplied_potion = Potion({}, self.duration)
101        multiplied_potion.effects = effects_multiplied_intesities
102        self.used_in_reation = True
103        return multiplied_potion
104
105    @staticmethod
106    def round_float(float_number):
107        if isinstance(float_number, float):
108            if float_number - math.floor(float_number) <= 0.5:
109                return math.floor(float_number)
110            else:
111                return math.ceil(float_number)
112        else:
113            raise TypeError("This function works only with float numbers.")
114
115    @_potion_validation_check
116    def delution(self, factor):
117        diluted_effects = {
118            effect_name: [
119                effect_intensity[0],
120                Potion.round_float(effect_intensity[1] * factor),
121            ]
122            for effect_name, effect_intensity in self.effects.items()
123        }
124        diluted_potion = Potion({}, self.duration)
125        diluted_potion.effects = diluted_effects
126        self.used_in_reation = True
127        return diluted_potion
128
129    @_potion_validation_check
130    def __sub__(self, other):
131        if not isinstance(other, Potion):
132            raise TypeError("Subtraction is only supported between Potion instances.")
133
134        if any(effect_name not in self.effects for effect_name in other.effects):
135            raise TypeError(
136                "The right-hand side Potion has effects not present in the left-hand side Potion."
137            )
138
139        subtracted_effects = self.effects
140        for effect_name, effect_intesity in other.effects.items():
141            updated_intesity = subtracted_effects[effect_name][1] - effect_intesity[1]
142            if updated_intesity <= 0:
143                del subtracted_effects[effect_name]
144            else:
145                subtracted_effects[effect_name][1] = updated_intesity
146
147        subtracted_duration = (
148            self.duration
149        )  # Duration remains the same as the left-hand side Potion
150
151        subtracted_potion = Potion({}, subtracted_duration)
152        subtracted_potion.effects = subtracted_effects
153        self.used_in_reation = True
154        other.used_in_reation = True
155        return subtracted_potion
156
157    def __mul__(self, factor):
158        if factor < 0:
159            raise TypeError("Invalid parameter.")
160        elif isinstance(factor, float) and factor > 0 and factor < 1:
161            return self.delution(factor)
162        elif isinstance(factor, int):
163            return self.multiplication(factor)
164        else:
165            raise TypeError("Invalid parameter.")
166
167    @_potion_validation_check
168    def __truediv__(self, number):
169        if not isinstance(number, int):
170            raise TypeError("You can divide the Potion only with whole number.")
171        divided_effects = {
172            effect_name: [
173                effect_intesity[0],
174                Potion.round_float(effect_intesity[1] / number),
175            ]
176            for effect_name, effect_intesity in self.effects.items()
177        }
178        new_potions = []
179        for _ in range(number):
180            potion = Potion({}, self.duration)
181            potion.effects = copy.deepcopy(divided_effects)
182            new_potions.append(potion)
183
184        self.used_in_reation = True
185        return tuple(new_potions)
186
187    @property
188    def _total_intensity(self):
189        return sum(
190            intensity
191            for _, (func, intensity) in self.effects.items()
192            if func is not None
193        )
194
195    @_potion_validation_check
196    def __lt__(self, other):
197        if not isinstance(other, Potion):
198            raise TypeError("Comparison is only supported between Potion instances.")
199        return self._total_intensity < other._total_intensity
200
201    @_potion_validation_check
202    def __eq__(self, other):
203        if not isinstance(other, Potion):
204            return False
205        return all(
206            effect_name in self.effects
207            and effect_intensity[1] == self.effects[effect_name][1]
208            for effect_name, effect_intensity in other.effects.items()
209        )
210
211    def __deepcopy__(self, memo):
212        new_potion = type(self).__new__(type(self))
213        memo[id(self)] = new_potion
214        new_potion.__dict__.update(copy.deepcopy(self.__dict__, memo))
215        return new_potion
216
217    def __dir__(self):
218        return self.effects
219
220
221class ГоспожатаПоХимия:
222    def __init__(self):
223        self.targets = {}
224
225    # calculate the molecular mass of every effect
226    def molecular_mass(self, effect_name):
227        if not isinstance(effect_name, str):
228            raise TypeError("Funtion works only with string.")
229        return sum(ord(char) for char in effect_name)
230
231    # how the target will be if havent taken anything
232    def original_state(self, target):
233        return copy.deepcopy(target.__dict__)
234
235    # Restore the sober state of the target
236    def restore_original_state(self, target_id):
237        target_dict_copy = copy.deepcopy(self.targets[target_id][1])
238        self.targets[target_id][0].__dict__ = target_dict_copy
239
240    # Keep an record for every potion that the target is ON at that moment
241    def poitions_in_the_blood(self, target, potion):
242        if id(target) not in self.targets:
243            self.targets[id(target)] = [
244                target,
245                self.original_state(target),
246                OrderedDict(),
247            ]
248            self.targets[id(target)][2][id(potion)] = copy.deepcopy(potion)
249        else:
250            self.targets[id(target)][2][id(potion)] = copy.deepcopy(potion)
251
252    # special apply by the teacher :)
253    def apply_potions(self, target_id):
254        potions_applied_copy = copy.deepcopy(
255            list(self.targets[target_id][2].values())
256        )  # Use deepcopy
257        target = self.targets[target_id][0]
258        for applied_potion in potions_applied_copy:
259            sorted_effects = sorted(
260                applied_potion.effects.keys(), key=self.molecular_mass, reverse=True
261            )
262            for effect_name in sorted_effects:
263                if applied_potion.effects[effect_name][0] is not None:
264                    getattr(applied_potion, effect_name)(target)
265
266    def update_state(self, targets_to_update):
267        for target_id in targets_to_update:
268            self.restore_original_state(target_id)
269            self.apply_potions(target_id)
270
271    # tick-tac
272    def tick(self):
273        targets_to_update = []
274        for target_id, [
275            target,
276            target_original_state,
277            potions_applied,
278        ] in self.targets.items():
279            potions_to_remove = []
280            for potion_id, potion in list(potions_applied.items()):
281                potion.duration -= 1
282                if potion.duration <= 0:
283                    potions_to_remove.append(potion_id)
284                    targets_to_update.append(target_id)
285            for potion_id in potions_to_remove:
286                del potions_applied[potion_id]
287        self.update_state(targets_to_update)
288
289    def apply(self, target, potion):
290        if not isinstance(potion, Potion):
291            raise TypeError("Object must be of type Potion to be applied.")
292        if potion.depleted:
293            raise TypeError("Potion is depleted.")
294        if potion.used_in_reation:
295            raise TypeError("Potion is now part of something bigger than itself.")
296        if potion.duration > 0:
297            self.poitions_in_the_blood(target, potion)
298            sorted_effects = sorted(
299                potion.effects.keys(), key=self.molecular_mass, reverse=True
300            )
301            for effect_name in sorted_effects:
302                if potion.effects[effect_name][0] is not None:
303                    getattr(potion, effect_name)(target)
304            potion.depleted = True

.......E............
======================================================================
ERROR: test_deprecation (test.TestPotionOperations)
Test deprecation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 271, in test_deprecation
potion1.int_attr_fun(self._target)
File "/tmp/solution.py", line 26, in __getattr__
raise AttributeError(f"Object doesn't have attribute {attribute_name}.")
AttributeError: Object doesn't have attribute int_attr_fun.

----------------------------------------------------------------------
Ran 20 tests in 0.003s

FAILED (errors=1)

Дискусия
История

n1from typing import Anyn
2import math1import math
3import copy2import copy
4from collections import OrderedDict3from collections import OrderedDict
54
65
7class Potion:6class Potion:
n8    potions_used_in_reactions = set()n
9 
10    def __init__(self, effects, duration):7    def __init__(self, effects, duration):
11        self.effects = {effect: [func, 1] for effect, func in effects.items()}8        self.effects = {effect: [func, 1] for effect, func in effects.items()}
12        self.duration = duration9        self.duration = duration
nn10        self.used_in_reation = False
11        self.depleted = False
1312
14    def __getattr__(self, attribute_name):13    def __getattr__(self, attribute_name):
15        if (14        if (
16            attribute_name in self.effects15            attribute_name in self.effects
17            and self.effects[attribute_name][0] is not None16            and self.effects[attribute_name][0] is not None
18        ):17        ):
19            return lambda target: self._apply_effect(18            return lambda target: self._apply_effect(
20                target, attribute_name, self.effects[attribute_name][0]19                target, attribute_name, self.effects[attribute_name][0]
21            )20            )
n22        elif Potion._check_if_depleted(self):n21        elif self.depleted == True:
23            raise TypeError("Potion is depleted.")22            raise TypeError("Potion is depleted.")
24        elif attribute_name in self.effects and self.effects[attribute_name][0] is None:23        elif attribute_name in self.effects and self.effects[attribute_name][0] is None:
25            raise TypeError("Effect is depleted.")24            raise TypeError("Effect is depleted.")
26        else:25        else:
27            raise AttributeError(f"Object doesn't have attribute {attribute_name}.")26            raise AttributeError(f"Object doesn't have attribute {attribute_name}.")
2827
n29    @staticmethodn
30    def _is_used_in_reation(potion):
31        if not isinstance(potion, Potion):
32            raise TypeError("Object must be of type Potion.")
33        return id(potion) in Potion.potions_used_in_reactions
34 
35    @staticmethod
36    def _check_if_depleted(potion):
37        if not isinstance(potion, Potion):
38            raise TypeError("Object must be of type Potion.")
39        return all(
40            potion.effects[effect_name][0] is None
41            for effect_name, (_, _) in potion.effects.items()
42        )
43 
44    def _potion_validation_check(func):28    def _potion_validation_check(func):
45        def wrapper(*args, **kwargs):29        def wrapper(*args, **kwargs):
n46            if Potion._is_used_in_reation(args[0]):n30            if args[0].used_in_reation == True:
47                raise TypeError("Potion is now part of something bigger than itself.")31                raise TypeError("Potion is now part of something bigger than itself.")
n48            elif isinstance(args[1], Potion) and Potion._is_used_in_reation(args[1]):n32            elif isinstance(args[1], Potion) and args[1].used_in_reation == True:
49                raise TypeError("Potion is now part of something bigger than itself.")33                raise TypeError("Potion is now part of something bigger than itself.")
n50            elif Potion._check_if_depleted(args[0]):n34            elif args[0].depleted == True:
51                raise TypeError("Potion is depleted.")35                raise TypeError("Potion is depleted.")
n52            elif isinstance(args[1], Potion) and Potion._check_if_depleted(args[1]):n36            elif isinstance(args[1], Potion) and args[1].depleted == True:
53                raise TypeError("Potion is depleted.")37                raise TypeError("Potion is depleted.")
54            return func(*args, **kwargs)38            return func(*args, **kwargs)
5539
56        return wrapper40        return wrapper
5741
58    @_potion_validation_check42    @_potion_validation_check
59    def _apply_effect(self, target, effect_name, effect):43    def _apply_effect(self, target, effect_name, effect):
60        for _ in range(self.effects[effect_name][1]):44        for _ in range(self.effects[effect_name][1]):
61            effect(target)45            effect(target)
n62        self.effects[effect_name][0] = None  # we mark the used effect function as Nonen46        self.effects[effect_name][0] = None
47        self.effects[effect_name][1] = 0
48        # we mark the used effect function as None
49        # and intesity as none
6350
64    @_potion_validation_check51    @_potion_validation_check
65    def __add__(self, other):52    def __add__(self, other):
66        if not isinstance(other, Potion):53        if not isinstance(other, Potion):
67            raise TypeError("Addition is only supported between Potion instances.")54            raise TypeError("Addition is only supported between Potion instances.")
68        combined_effects = self.effects55        combined_effects = self.effects
6956
70        for effect_name, effect_intensity in other.effects.items():57        for effect_name, effect_intensity in other.effects.items():
71            if effect_name not in combined_effects:58            if effect_name not in combined_effects:
72                combined_effects[effect_name] = effect_intensity59                combined_effects[effect_name] = effect_intensity
73            else:60            else:
74                if (61                if (
75                    combined_effects[effect_name][0] is None62                    combined_effects[effect_name][0] is None
76                    and effect_intensity[0] is None63                    and effect_intensity[0] is None
77                ):64                ):
78                    combined_effects[effect_name] = [65                    combined_effects[effect_name] = [
79                        None,66                        None,
80                        combined_effects[effect_name][1] + effect_intensity[1],67                        combined_effects[effect_name][1] + effect_intensity[1],
81                    ]68                    ]
82                elif (69                elif (
83                    combined_effects[effect_name][0] is not None70                    combined_effects[effect_name][0] is not None
84                    and effect_intensity[0] is None71                    and effect_intensity[0] is None
85                ):72                ):
86                    pass73                    pass
87                elif (74                elif (
88                    combined_effects[effect_name][0] is None75                    combined_effects[effect_name][0] is None
89                    and effect_intensity[0] is not None76                    and effect_intensity[0] is not None
90                ):77                ):
91                    combined_effects[effect_name] = [78                    combined_effects[effect_name] = [
92                        effect_intensity[0],79                        effect_intensity[0],
93                        effect_intensity[1],80                        effect_intensity[1],
94                    ]81                    ]
95                else:82                else:
96                    combined_effects[effect_name][1] = (83                    combined_effects[effect_name][1] = (
97                        combined_effects[effect_name][1] + effect_intensity[1]84                        combined_effects[effect_name][1] + effect_intensity[1]
98                    )85                    )
9986
100        combined_duration = max(self.duration, other.duration)87        combined_duration = max(self.duration, other.duration)
101        combined_potion = Potion({}, combined_duration)88        combined_potion = Potion({}, combined_duration)
102        combined_potion.effects = combined_effects89        combined_potion.effects = combined_effects
n103        Potion.potions_used_in_reactions.add(id(self))n90        self.used_in_reation = True
104        Potion.potions_used_in_reactions.add(id(other))91        other.used_in_reation = True
105        return combined_potion92        return combined_potion
10693
107    @_potion_validation_check94    @_potion_validation_check
108    def multiplication(self, factor):95    def multiplication(self, factor):
109        effects_multiplied_intesities = {96        effects_multiplied_intesities = {
110            effect: [func_intensity[0], func_intensity[1] * factor]97            effect: [func_intensity[0], func_intensity[1] * factor]
111            for effect, func_intensity in self.effects.items()98            for effect, func_intensity in self.effects.items()
112        }99        }
113        multiplied_potion = Potion({}, self.duration)100        multiplied_potion = Potion({}, self.duration)
114        multiplied_potion.effects = effects_multiplied_intesities101        multiplied_potion.effects = effects_multiplied_intesities
n115        Potion.potions_used_in_reactions.add(id(self))n102        self.used_in_reation = True
116        return multiplied_potion103        return multiplied_potion
117104
118    @staticmethod105    @staticmethod
119    def round_float(float_number):106    def round_float(float_number):
120        if isinstance(float_number, float):107        if isinstance(float_number, float):
121            if float_number - math.floor(float_number) <= 0.5:108            if float_number - math.floor(float_number) <= 0.5:
122                return math.floor(float_number)109                return math.floor(float_number)
123            else:110            else:
124                return math.ceil(float_number)111                return math.ceil(float_number)
125        else:112        else:
126            raise TypeError("This function works only with float numbers.")113            raise TypeError("This function works only with float numbers.")
127114
128    @_potion_validation_check115    @_potion_validation_check
129    def delution(self, factor):116    def delution(self, factor):
130        diluted_effects = {117        diluted_effects = {
131            effect_name: [118            effect_name: [
132                effect_intensity[0],119                effect_intensity[0],
133                Potion.round_float(effect_intensity[1] * factor),120                Potion.round_float(effect_intensity[1] * factor),
134            ]121            ]
135            for effect_name, effect_intensity in self.effects.items()122            for effect_name, effect_intensity in self.effects.items()
136        }123        }
137        diluted_potion = Potion({}, self.duration)124        diluted_potion = Potion({}, self.duration)
138        diluted_potion.effects = diluted_effects125        diluted_potion.effects = diluted_effects
n139        Potion.potions_used_in_reactions.add(id(self))n126        self.used_in_reation = True
140        return diluted_potion127        return diluted_potion
141128
142    @_potion_validation_check129    @_potion_validation_check
143    def __sub__(self, other):130    def __sub__(self, other):
144        if not isinstance(other, Potion):131        if not isinstance(other, Potion):
145            raise TypeError("Subtraction is only supported between Potion instances.")132            raise TypeError("Subtraction is only supported between Potion instances.")
146133
147        if any(effect_name not in self.effects for effect_name in other.effects):134        if any(effect_name not in self.effects for effect_name in other.effects):
148            raise TypeError(135            raise TypeError(
149                "The right-hand side Potion has effects not present in the left-hand side Potion."136                "The right-hand side Potion has effects not present in the left-hand side Potion."
150            )137            )
151138
152        subtracted_effects = self.effects139        subtracted_effects = self.effects
153        for effect_name, effect_intesity in other.effects.items():140        for effect_name, effect_intesity in other.effects.items():
154            updated_intesity = subtracted_effects[effect_name][1] - effect_intesity[1]141            updated_intesity = subtracted_effects[effect_name][1] - effect_intesity[1]
155            if updated_intesity <= 0:142            if updated_intesity <= 0:
156                del subtracted_effects[effect_name]143                del subtracted_effects[effect_name]
157            else:144            else:
158                subtracted_effects[effect_name][1] = updated_intesity145                subtracted_effects[effect_name][1] = updated_intesity
159146
160        subtracted_duration = (147        subtracted_duration = (
161            self.duration148            self.duration
162        )  # Duration remains the same as the left-hand side Potion149        )  # Duration remains the same as the left-hand side Potion
163150
164        subtracted_potion = Potion({}, subtracted_duration)151        subtracted_potion = Potion({}, subtracted_duration)
165        subtracted_potion.effects = subtracted_effects152        subtracted_potion.effects = subtracted_effects
n166        Potion.potions_used_in_reactions.add(id(self))n153        self.used_in_reation = True
167        Potion.potions_used_in_reactions.add(id(other))154        other.used_in_reation = True
168        return subtracted_potion155        return subtracted_potion
169156
170    def __mul__(self, factor):157    def __mul__(self, factor):
171        if factor < 0:158        if factor < 0:
172            raise TypeError("Invalid parameter.")159            raise TypeError("Invalid parameter.")
173        elif isinstance(factor, float) and factor > 0 and factor < 1:160        elif isinstance(factor, float) and factor > 0 and factor < 1:
174            return self.delution(factor)161            return self.delution(factor)
175        elif isinstance(factor, int):162        elif isinstance(factor, int):
176            return self.multiplication(factor)163            return self.multiplication(factor)
177        else:164        else:
178            raise TypeError("Invalid parameter.")165            raise TypeError("Invalid parameter.")
179166
180    @_potion_validation_check167    @_potion_validation_check
181    def __truediv__(self, number):168    def __truediv__(self, number):
182        if not isinstance(number, int):169        if not isinstance(number, int):
183            raise TypeError("You can divide the Potion only with whole number.")170            raise TypeError("You can divide the Potion only with whole number.")
184        divided_effects = {171        divided_effects = {
185            effect_name: [172            effect_name: [
186                effect_intesity[0],173                effect_intesity[0],
187                Potion.round_float(effect_intesity[1] / number),174                Potion.round_float(effect_intesity[1] / number),
188            ]175            ]
189            for effect_name, effect_intesity in self.effects.items()176            for effect_name, effect_intesity in self.effects.items()
190        }177        }
191        new_potions = []178        new_potions = []
192        for _ in range(number):179        for _ in range(number):
193            potion = Potion({}, self.duration)180            potion = Potion({}, self.duration)
194            potion.effects = copy.deepcopy(divided_effects)181            potion.effects = copy.deepcopy(divided_effects)
195            new_potions.append(potion)182            new_potions.append(potion)
196183
n197        Potion.potions_used_in_reactions.add(id(self))n184        self.used_in_reation = True
198        return tuple(new_potions)185        return tuple(new_potions)
199186
200    @property187    @property
201    def _total_intensity(self):188    def _total_intensity(self):
202        return sum(189        return sum(
203            intensity190            intensity
204            for _, (func, intensity) in self.effects.items()191            for _, (func, intensity) in self.effects.items()
205            if func is not None192            if func is not None
206        )193        )
207194
208    @_potion_validation_check195    @_potion_validation_check
209    def __lt__(self, other):196    def __lt__(self, other):
210        if not isinstance(other, Potion):197        if not isinstance(other, Potion):
211            raise TypeError("Comparison is only supported between Potion instances.")198            raise TypeError("Comparison is only supported between Potion instances.")
212        return self._total_intensity < other._total_intensity199        return self._total_intensity < other._total_intensity
213200
214    @_potion_validation_check201    @_potion_validation_check
215    def __eq__(self, other):202    def __eq__(self, other):
216        if not isinstance(other, Potion):203        if not isinstance(other, Potion):
217            return False204            return False
218        return all(205        return all(
219            effect_name in self.effects206            effect_name in self.effects
220            and effect_intensity[1] == self.effects[effect_name][1]207            and effect_intensity[1] == self.effects[effect_name][1]
221            for effect_name, effect_intensity in other.effects.items()208            for effect_name, effect_intensity in other.effects.items()
222        )209        )
223210
224    def __deepcopy__(self, memo):211    def __deepcopy__(self, memo):
225        new_potion = type(self).__new__(type(self))212        new_potion = type(self).__new__(type(self))
226        memo[id(self)] = new_potion213        memo[id(self)] = new_potion
227        new_potion.__dict__.update(copy.deepcopy(self.__dict__, memo))214        new_potion.__dict__.update(copy.deepcopy(self.__dict__, memo))
228        return new_potion215        return new_potion
nn216 
217    def __dir__(self):
218        return self.effects
229219
230220
231class ГоспожатаПоХимия:221class ГоспожатаПоХимия:
232    def __init__(self):222    def __init__(self):
233        self.targets = {}223        self.targets = {}
234224
235    # calculate the molecular mass of every effect225    # calculate the molecular mass of every effect
236    def molecular_mass(self, effect_name):226    def molecular_mass(self, effect_name):
237        if not isinstance(effect_name, str):227        if not isinstance(effect_name, str):
238            raise TypeError("Funtion works only with string.")228            raise TypeError("Funtion works only with string.")
239        return sum(ord(char) for char in effect_name)229        return sum(ord(char) for char in effect_name)
240230
241    # how the target will be if havent taken anything231    # how the target will be if havent taken anything
242    def original_state(self, target):232    def original_state(self, target):
243        return copy.deepcopy(target.__dict__)233        return copy.deepcopy(target.__dict__)
244234
245    # Restore the sober state of the target235    # Restore the sober state of the target
246    def restore_original_state(self, target_id):236    def restore_original_state(self, target_id):
247        target_dict_copy = copy.deepcopy(self.targets[target_id][1])237        target_dict_copy = copy.deepcopy(self.targets[target_id][1])
248        self.targets[target_id][0].__dict__ = target_dict_copy238        self.targets[target_id][0].__dict__ = target_dict_copy
249239
250    # Keep an record for every potion that the target is ON at that moment240    # Keep an record for every potion that the target is ON at that moment
251    def poitions_in_the_blood(self, target, potion):241    def poitions_in_the_blood(self, target, potion):
252        if id(target) not in self.targets:242        if id(target) not in self.targets:
253            self.targets[id(target)] = [243            self.targets[id(target)] = [
254                target,244                target,
255                self.original_state(target),245                self.original_state(target),
256                OrderedDict(),246                OrderedDict(),
257            ]247            ]
258            self.targets[id(target)][2][id(potion)] = copy.deepcopy(potion)248            self.targets[id(target)][2][id(potion)] = copy.deepcopy(potion)
259        else:249        else:
260            self.targets[id(target)][2][id(potion)] = copy.deepcopy(potion)250            self.targets[id(target)][2][id(potion)] = copy.deepcopy(potion)
261251
262    # special apply by the teacher :)252    # special apply by the teacher :)
263    def apply_potions(self, target_id):253    def apply_potions(self, target_id):
264        potions_applied_copy = copy.deepcopy(254        potions_applied_copy = copy.deepcopy(
265            list(self.targets[target_id][2].values())255            list(self.targets[target_id][2].values())
266        )  # Use deepcopy256        )  # Use deepcopy
267        target = self.targets[target_id][0]257        target = self.targets[target_id][0]
268        for applied_potion in potions_applied_copy:258        for applied_potion in potions_applied_copy:
269            sorted_effects = sorted(259            sorted_effects = sorted(
270                applied_potion.effects.keys(), key=self.molecular_mass, reverse=True260                applied_potion.effects.keys(), key=self.molecular_mass, reverse=True
271            )261            )
272            for effect_name in sorted_effects:262            for effect_name in sorted_effects:
273                if applied_potion.effects[effect_name][0] is not None:263                if applied_potion.effects[effect_name][0] is not None:
274                    getattr(applied_potion, effect_name)(target)264                    getattr(applied_potion, effect_name)(target)
275265
276    def update_state(self, targets_to_update):266    def update_state(self, targets_to_update):
277        for target_id in targets_to_update:267        for target_id in targets_to_update:
278            self.restore_original_state(target_id)268            self.restore_original_state(target_id)
279            self.apply_potions(target_id)269            self.apply_potions(target_id)
280270
281    # tick-tac271    # tick-tac
282    def tick(self):272    def tick(self):
283        targets_to_update = []273        targets_to_update = []
284        for target_id, [274        for target_id, [
285            target,275            target,
286            target_original_state,276            target_original_state,
287            potions_applied,277            potions_applied,
288        ] in self.targets.items():278        ] in self.targets.items():
289            potions_to_remove = []279            potions_to_remove = []
290            for potion_id, potion in list(potions_applied.items()):280            for potion_id, potion in list(potions_applied.items()):
291                potion.duration -= 1281                potion.duration -= 1
292                if potion.duration <= 0:282                if potion.duration <= 0:
293                    potions_to_remove.append(potion_id)283                    potions_to_remove.append(potion_id)
294                    targets_to_update.append(target_id)284                    targets_to_update.append(target_id)
295            for potion_id in potions_to_remove:285            for potion_id in potions_to_remove:
296                del potions_applied[potion_id]286                del potions_applied[potion_id]
297        self.update_state(targets_to_update)287        self.update_state(targets_to_update)
298288
299    def apply(self, target, potion):289    def apply(self, target, potion):
300        if not isinstance(potion, Potion):290        if not isinstance(potion, Potion):
301            raise TypeError("Object must be of type Potion to be applied.")291            raise TypeError("Object must be of type Potion to be applied.")
n302        if Potion._check_if_depleted(potion):n292        if potion.depleted:
303            raise TypeError("Potion is depleted.")293            raise TypeError("Potion is depleted.")
n304        if Potion._is_used_in_reation(potion):n294        if potion.used_in_reation:
305            raise TypeError("Potion is now part of something bigger than itself.")295            raise TypeError("Potion is now part of something bigger than itself.")
306        if potion.duration > 0:296        if potion.duration > 0:
307            self.poitions_in_the_blood(target, potion)297            self.poitions_in_the_blood(target, potion)
308            sorted_effects = sorted(298            sorted_effects = sorted(
309                potion.effects.keys(), key=self.molecular_mass, reverse=True299                potion.effects.keys(), key=self.molecular_mass, reverse=True
310            )300            )
311            for effect_name in sorted_effects:301            for effect_name in sorted_effects:
312                if potion.effects[effect_name][0] is not None:302                if potion.effects[effect_name][0] is not None:
313                    getattr(potion, effect_name)(target)303                    getattr(potion, effect_name)(target)
tt304            potion.depleted = True
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op