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

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

11 точки общо

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

  1import math
  2
  3class Potion:
  4    def __init__(self, effects, duration):
  5        self.effects = effects
  6        self.duration = duration
  7        self.intensities = {}
  8        self.used_effects = set()
  9        self.used = False
 10        self.depleted = False
 11
 12        for name in effects.keys():
 13            self.intensities[name] = self.intensities.get(name, 0) + 1
 14
 15    def __getattr__(self, name):
 16        if name not in self.effects.keys():
 17            raise AttributeError("Attribute not found.")
 18        if self.used:
 19            raise TypeError("Potion is now part of something bigger than itself.")
 20        if self.depleted:
 21            raise TypeError("Potion is depleted.")
 22        if name in self.used_effects:
 23            raise TypeError("Effect is depleted.")
 24
 25        def execute_n_times(func):
 26            def wrapper(target):
 27                for _ in range(int(self.intensities.get(name))):
 28                    func(target)
 29            return wrapper
 30        self.used_effects.add(name)
 31        return execute_n_times(self.effects.get(name))
 32
 33    def __add__(self, other):
 34        validate(self)
 35        validate(other)
 36
 37        new_duration = max(self.duration, other.duration)
 38        combined_effects = {}
 39        intensities = {}
 40        used_effects = set()
 41
 42        for name, usage in self.effects.items():
 43            combined_effects[name] = usage
 44            intensities[name] = intensities.get(name) + 1 if name in intensities else self.intensities.get(name, 0)
 45            used_effects = used_effects.union(self.used_effects)
 46
 47        for name, usage in other.effects.items():
 48            combined_effects[name] = usage
 49            intensities[name] = intensities.get(name) + 1 if name in intensities else other.intensities.get(name, 0)
 50            used_effects = used_effects.union(other.used_effects)
 51
 52        self.used = True
 53        other.used = True
 54        potion = Potion(combined_effects, new_duration)
 55        potion.intensities = intensities
 56        potion.used_effects = used_effects
 57        if len(potion.used_effects) == len(potion.effects):
 58            potion.depleted = True
 59        return potion
 60
 61    def __mul__(self, other):
 62        validate(self)
 63
 64        if isinstance(other, (int, float)):
 65            potion = Potion(self.effects.copy(), self.duration)
 66            intensities = {}
 67
 68            for name, value in self.intensities.items():
 69                intensities[name] = value * other
 70                if 0 < other < 1:
 71                    intensities[name] = self._round_value(intensities[name])
 72
 73            potion.intensities = intensities
 74            potion.used_effects = self.used_effects.copy()
 75            if len(potion.used_effects) == len(potion.effects):
 76                potion.depleted = True
 77            self.used = True
 78            return potion
 79
 80    def __sub__(self, other):
 81        validate(self)
 82        validate(other)
 83
 84        new_duration = other.duration
 85        purified_effects = {}
 86        intensities = {}
 87        used_effects = set()
 88
 89        for name, usage in other.effects.items():
 90            if name not in self.effects:
 91                raise TypeError("Cannot purify potion.")
 92            if other.intensities.get(name) >= self.intensities.get(name):
 93                continue
 94            else:
 95                purified_effects[name] = usage
 96                intensities[name] = self.intensities.get(name) - other.intensities.get(name)
 97                if name in other.used_effects or name in self.used_effects:
 98                    used_effects.add(name)
 99
100        for name, usage in self.effects.items():
101            if name not in other.effects.keys():
102                purified_effects[name] = usage
103                intensities[name] = self.intensities.get(name)
104                if name in self.used_effects:
105                    used_effects.add(name)
106        
107        self.used = True
108        other.used = True
109        potion = Potion(purified_effects, new_duration)
110        potion.intensities = intensities
111        potion.used_effects = used_effects
112        if len(potion.used_effects) == len(potion.effects):
113            potion.depleted = True
114        return potion
115
116    def __truediv__(self, other):
117        validate(self)
118
119        if isinstance(other, (int, float)):
120            intensities = {}
121
122            for name, intensity in self.intensities.items():
123                intensities[name] = self._round_value(intensity / other)
124
125            result = tuple(Potion(self.effects.copy(), self.duration) for _ in range(other))
126            for potion in result:
127                potion.intensities = intensities.copy()
128                potion.used_effects = self.used_effects.copy()
129                if len(potion.used_effects) == len(potion.effects):
130                    potion.depleted = True
131            self.used = True
132            return result
133
134    
135    def __eq__(self, other):
136        validate(self)
137        validate(other)
138
139        if not isinstance(other, Potion):
140            return False
141
142        if len(self.effects) != len(other.effects):
143            return False
144
145        for name in self.effects.keys():
146            if name not in other.effects:
147                return False
148            if self.intensities.get(name) != other.intensities.get(name):
149                if name in self.used_effects and name in other.used_effects:
150                    continue
151                if (name in self.used_effects and other.intensities.get(name) == 0 or
152                    name in other.used_effects and self.intensities.get(name) == 0):
153                    continue
154                return False
155        return True
156
157    def __lt__(self, other):
158        validate(self)
159        validate(other)
160
161        if not isinstance(other, Potion):
162            return False
163
164        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])
165        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])
166        return first_sum < second_sum
167
168    def __gt__(self, other):
169        validate(self)
170        validate(other)
171
172        if not isinstance(other, Potion):
173            return False
174
175        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])
176        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])
177        return first_sum > second_sum
178
179    def __hash__(self):
180        return super().__hash__()
181    
182    def _round_value(self, value):
183        if value % 1 > 0.5:
184            return int(math.ceil(value))
185        return int(math.floor(value))
186
187def validate(potion):
188    if potion.depleted:
189        raise TypeError("Potion is depleted.")
190    if potion.used:
191        raise TypeError("Potion is now part of something bigger than itself.")
192    
193def ascii_sum(string):
194    return sum(ord(char) for char in string)
195
196class ГоспожатаПоХимия:
197    # {
198    #   target: { initial_state },
199    #   target2: { initial_state }
200    # }
201    _target_states = {}
202
203    # {
204    #   target: [potion1, potion2],
205    #   target2: [potion3]
206    # }
207    _target_potions = {}
208
209    # {
210    #   target: {
211    #           potion1: time1,
212    #           potion2: time2 
213    #       },
214    #   target2: {
215    #           potion3: time3
216    #       }
217    # }
218    _times = {}
219
220    def apply(self, target, potion):
221        validate(potion)
222
223        if target not in self._target_states:
224            self.save_initial_state(target)
225        if target not in self._times.keys():
226            self._times[target] = {}
227        if target not in self._target_potions.keys():
228            self._target_potions[target] = []
229
230        self._times[target][potion] = potion.duration
231        self._target_potions[target].append(potion)
232        sorted_effects = sorted(list(potion.effects), key=ascii_sum, reverse=True)
233        for effect in sorted_effects:
234            try:
235                getattr(potion, effect)(target)
236            except TypeError:
237                continue
238        potion.depleted = True
239        if potion.duration == 0:
240            self.reset_target(target)
241
242    def _apply_all(self, target):
243        for potion in self._target_potions[target]:
244            if potion.duration <= 0:
245                continue
246            for effect in sorted(list(potion.effects), key=ascii_sum, reverse=True):
247                potion.effects[effect](target)
248
249    def tick(self):
250        for target, potions in self._times.items():
251            for potion, time in potions.items():
252                self._times[target][potion] = time - 1
253        
254        for target, potions in (self._times.copy()).items():
255            for potion, time in (potions.copy()).items():
256                if time <= 0:
257                    self._times[target].pop(potion, None)
258                    self._target_potions[target].remove(potion)
259                    if len(self._times[target]) == 0:
260                        self._times.pop(target, None)
261                    self.reset_target(target)
262                    if target not in self._times:
263                        self._target_potions.pop(target, None)
264                        self._target_states.pop(target, None)
265
266    def save_initial_state(self, target):
267        self._target_states[target] = {attr: getattr(target, attr) for attr in dir(target) if not attr.startswith('_')}
268    
269    def reset_target(self, target):
270        for attr, initial_value in self._target_states[target].items():
271            setattr(target, attr, initial_value)
272        for attr in dir(target):
273            if not attr.startswith('_') and attr not in self._target_states[target].keys():
274                try:
275                    delattr(target, attr)
276                except AttributeError:
277                    continue
278
279        self._apply_all(target)

...................F
======================================================================
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.002s

FAILED (failures=1)

Дискусия
Георги Кунчев
04.12.2023 11:24

Доста чисто решение. Имах някакви дребни коментари, но цялостният стил е супер.
История

f1import mathf1import math
22
3class Potion:3class Potion:
4    def __init__(self, effects, duration):4    def __init__(self, effects, duration):
5        self.effects = effects5        self.effects = effects
6        self.duration = duration6        self.duration = duration
7        self.intensities = {}7        self.intensities = {}
8        self.used_effects = set()8        self.used_effects = set()
9        self.used = False9        self.used = False
10        self.depleted = False10        self.depleted = False
1111
12        for name in effects.keys():12        for name in effects.keys():
13            self.intensities[name] = self.intensities.get(name, 0) + 113            self.intensities[name] = self.intensities.get(name, 0) + 1
1414
15    def __getattr__(self, name):15    def __getattr__(self, name):
16        if name not in self.effects.keys():16        if name not in self.effects.keys():
17            raise AttributeError("Attribute not found.")17            raise AttributeError("Attribute not found.")
18        if self.used:18        if self.used:
19            raise TypeError("Potion is now part of something bigger than itself.")19            raise TypeError("Potion is now part of something bigger than itself.")
nn20        if self.depleted:
21            raise TypeError("Potion is depleted.")
20        if name in self.used_effects:22        if name in self.used_effects:
21            raise TypeError("Effect is depleted.")23            raise TypeError("Effect is depleted.")
2224
23        def execute_n_times(func):25        def execute_n_times(func):
24            def wrapper(target):26            def wrapper(target):
25                for _ in range(int(self.intensities.get(name))):27                for _ in range(int(self.intensities.get(name))):
26                    func(target)28                    func(target)
27            return wrapper29            return wrapper
28        self.used_effects.add(name)30        self.used_effects.add(name)
n29        if len(self.used_effects) == len(self.effects):n
30            self.depleted = True
31        return execute_n_times(self.effects.get(name))31        return execute_n_times(self.effects.get(name))
3232
33    def __add__(self, other):33    def __add__(self, other):
34        validate(self)34        validate(self)
35        validate(other)35        validate(other)
3636
37        new_duration = max(self.duration, other.duration)37        new_duration = max(self.duration, other.duration)
38        combined_effects = {}38        combined_effects = {}
39        intensities = {}39        intensities = {}
40        used_effects = set()40        used_effects = set()
4141
42        for name, usage in self.effects.items():42        for name, usage in self.effects.items():
43            combined_effects[name] = usage43            combined_effects[name] = usage
44            intensities[name] = intensities.get(name) + 1 if name in intensities else self.intensities.get(name, 0)44            intensities[name] = intensities.get(name) + 1 if name in intensities else self.intensities.get(name, 0)
45            used_effects = used_effects.union(self.used_effects)45            used_effects = used_effects.union(self.used_effects)
4646
47        for name, usage in other.effects.items():47        for name, usage in other.effects.items():
48            combined_effects[name] = usage48            combined_effects[name] = usage
49            intensities[name] = intensities.get(name) + 1 if name in intensities else other.intensities.get(name, 0)49            intensities[name] = intensities.get(name) + 1 if name in intensities else other.intensities.get(name, 0)
50            used_effects = used_effects.union(other.used_effects)50            used_effects = used_effects.union(other.used_effects)
5151
52        self.used = True52        self.used = True
53        other.used = True53        other.used = True
54        potion = Potion(combined_effects, new_duration)54        potion = Potion(combined_effects, new_duration)
55        potion.intensities = intensities55        potion.intensities = intensities
56        potion.used_effects = used_effects56        potion.used_effects = used_effects
57        if len(potion.used_effects) == len(potion.effects):57        if len(potion.used_effects) == len(potion.effects):
58            potion.depleted = True58            potion.depleted = True
59        return potion59        return potion
6060
61    def __mul__(self, other):61    def __mul__(self, other):
62        validate(self)62        validate(self)
6363
64        if isinstance(other, (int, float)):64        if isinstance(other, (int, float)):
65            potion = Potion(self.effects.copy(), self.duration)65            potion = Potion(self.effects.copy(), self.duration)
66            intensities = {}66            intensities = {}
6767
68            for name, value in self.intensities.items():68            for name, value in self.intensities.items():
69                intensities[name] = value * other69                intensities[name] = value * other
70                if 0 < other < 1:70                if 0 < other < 1:
71                    intensities[name] = self._round_value(intensities[name])71                    intensities[name] = self._round_value(intensities[name])
7272
73            potion.intensities = intensities73            potion.intensities = intensities
74            potion.used_effects = self.used_effects.copy()74            potion.used_effects = self.used_effects.copy()
75            if len(potion.used_effects) == len(potion.effects):75            if len(potion.used_effects) == len(potion.effects):
76                potion.depleted = True76                potion.depleted = True
77            self.used = True77            self.used = True
78            return potion78            return potion
7979
80    def __sub__(self, other):80    def __sub__(self, other):
81        validate(self)81        validate(self)
82        validate(other)82        validate(other)
8383
84        new_duration = other.duration84        new_duration = other.duration
85        purified_effects = {}85        purified_effects = {}
86        intensities = {}86        intensities = {}
87        used_effects = set()87        used_effects = set()
8888
89        for name, usage in other.effects.items():89        for name, usage in other.effects.items():
90            if name not in self.effects:90            if name not in self.effects:
91                raise TypeError("Cannot purify potion.")91                raise TypeError("Cannot purify potion.")
92            if other.intensities.get(name) >= self.intensities.get(name):92            if other.intensities.get(name) >= self.intensities.get(name):
93                continue93                continue
94            else:94            else:
95                purified_effects[name] = usage95                purified_effects[name] = usage
96                intensities[name] = self.intensities.get(name) - other.intensities.get(name)96                intensities[name] = self.intensities.get(name) - other.intensities.get(name)
n97                if name in other.used_effects:n97                if name in other.used_effects or name in self.used_effects:
98                    used_effects.add(name)98                    used_effects.add(name)
9999
100        for name, usage in self.effects.items():100        for name, usage in self.effects.items():
101            if name not in other.effects.keys():101            if name not in other.effects.keys():
102                purified_effects[name] = usage102                purified_effects[name] = usage
103                intensities[name] = self.intensities.get(name)103                intensities[name] = self.intensities.get(name)
104                if name in self.used_effects:104                if name in self.used_effects:
105                    used_effects.add(name)105                    used_effects.add(name)
106        106        
107        self.used = True107        self.used = True
108        other.used = True108        other.used = True
109        potion = Potion(purified_effects, new_duration)109        potion = Potion(purified_effects, new_duration)
110        potion.intensities = intensities110        potion.intensities = intensities
111        potion.used_effects = used_effects111        potion.used_effects = used_effects
112        if len(potion.used_effects) == len(potion.effects):112        if len(potion.used_effects) == len(potion.effects):
n113                potion.depleted = Truen113            potion.depleted = True
114        return potion114        return potion
115115
116    def __truediv__(self, other):116    def __truediv__(self, other):
117        validate(self)117        validate(self)
118118
119        if isinstance(other, (int, float)):119        if isinstance(other, (int, float)):
120            intensities = {}120            intensities = {}
121121
122            for name, intensity in self.intensities.items():122            for name, intensity in self.intensities.items():
123                intensities[name] = self._round_value(intensity / other)123                intensities[name] = self._round_value(intensity / other)
124124
125            result = tuple(Potion(self.effects.copy(), self.duration) for _ in range(other))125            result = tuple(Potion(self.effects.copy(), self.duration) for _ in range(other))
126            for potion in result:126            for potion in result:
127                potion.intensities = intensities.copy()127                potion.intensities = intensities.copy()
128                potion.used_effects = self.used_effects.copy()128                potion.used_effects = self.used_effects.copy()
129                if len(potion.used_effects) == len(potion.effects):129                if len(potion.used_effects) == len(potion.effects):
130                    potion.depleted = True130                    potion.depleted = True
131            self.used = True131            self.used = True
132            return result132            return result
133133
134    134    
135    def __eq__(self, other):135    def __eq__(self, other):
136        validate(self)136        validate(self)
137        validate(other)137        validate(other)
138138
139        if not isinstance(other, Potion):139        if not isinstance(other, Potion):
140            return False140            return False
141141
142        if len(self.effects) != len(other.effects):142        if len(self.effects) != len(other.effects):
143            return False143            return False
144144
145        for name in self.effects.keys():145        for name in self.effects.keys():
146            if name not in other.effects:146            if name not in other.effects:
147                return False147                return False
148            if self.intensities.get(name) != other.intensities.get(name):148            if self.intensities.get(name) != other.intensities.get(name):
nn149                if name in self.used_effects and name in other.used_effects:
150                    continue
151                if (name in self.used_effects and other.intensities.get(name) == 0 or
152                    name in other.used_effects and self.intensities.get(name) == 0):
153                    continue
149                return False154                return False
150        return True155        return True
151156
152    def __lt__(self, other):157    def __lt__(self, other):
153        validate(self)158        validate(self)
154        validate(other)159        validate(other)
155160
156        if not isinstance(other, Potion):161        if not isinstance(other, Potion):
157            return False162            return False
158163
159        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])164        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])
160        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])165        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])
161        return first_sum < second_sum166        return first_sum < second_sum
162167
163    def __gt__(self, other):168    def __gt__(self, other):
164        validate(self)169        validate(self)
165        validate(other)170        validate(other)
166171
167        if not isinstance(other, Potion):172        if not isinstance(other, Potion):
168            return False173            return False
169174
170        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])175        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])
171        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])176        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])
172        return first_sum > second_sum177        return first_sum > second_sum
173178
174    def __hash__(self):179    def __hash__(self):
175        return super().__hash__()180        return super().__hash__()
176    181    
177    def _round_value(self, value):182    def _round_value(self, value):
178        if value % 1 > 0.5:183        if value % 1 > 0.5:
179            return int(math.ceil(value))184            return int(math.ceil(value))
180        return int(math.floor(value))185        return int(math.floor(value))
181186
182def validate(potion):187def validate(potion):
183    if potion.depleted:188    if potion.depleted:
184        raise TypeError("Potion is depleted.")189        raise TypeError("Potion is depleted.")
185    if potion.used:190    if potion.used:
186        raise TypeError("Potion is now part of something bigger than itself.")191        raise TypeError("Potion is now part of something bigger than itself.")
nn192    
193def ascii_sum(string):
194    return sum(ord(char) for char in string)
187195
188class ГоспожатаПоХимия:196class ГоспожатаПоХимия:
189    # {197    # {
190    #   target: { initial_state },198    #   target: { initial_state },
191    #   target2: { initial_state }199    #   target2: { initial_state }
192    # }200    # }
193    _target_states = {}201    _target_states = {}
194202
195    # {203    # {
196    #   target: [potion1, potion2],204    #   target: [potion1, potion2],
197    #   target2: [potion3]205    #   target2: [potion3]
198    # }206    # }
199    _target_potions = {}207    _target_potions = {}
200208
201    # {209    # {
202    #   target: {210    #   target: {
203    #           potion1: time1,211    #           potion1: time1,
204    #           potion2: time2 212    #           potion2: time2 
205    #       },213    #       },
206    #   target2: {214    #   target2: {
207    #           potion3: time3215    #           potion3: time3
208    #       }216    #       }
209    # }217    # }
210    _times = {}218    _times = {}
211219
212    def apply(self, target, potion):220    def apply(self, target, potion):
213        validate(potion)221        validate(potion)
214222
215        if target not in self._target_states:223        if target not in self._target_states:
216            self.save_initial_state(target)224            self.save_initial_state(target)
217        if target not in self._times.keys():225        if target not in self._times.keys():
218            self._times[target] = {}226            self._times[target] = {}
219        if target not in self._target_potions.keys():227        if target not in self._target_potions.keys():
220            self._target_potions[target] = []228            self._target_potions[target] = []
221229
222        self._times[target][potion] = potion.duration230        self._times[target][potion] = potion.duration
223        self._target_potions[target].append(potion)231        self._target_potions[target].append(potion)
n224        sorted_effects = list(sorted(potion.effects))n232        sorted_effects = sorted(list(potion.effects), key=ascii_sum, reverse=True)
225        for effect in sorted_effects:233        for effect in sorted_effects:
226            try:234            try:
227                getattr(potion, effect)(target)235                getattr(potion, effect)(target)
228            except TypeError:236            except TypeError:
229                continue237                continue
nn238        potion.depleted = True
239        if potion.duration == 0:
240            self.reset_target(target)
230241
231    def _apply_all(self, target):242    def _apply_all(self, target):
232        for potion in self._target_potions[target]:243        for potion in self._target_potions[target]:
n233            for effect in list(sorted(potion.effects)):n244            if potion.duration <= 0:
245                continue
246            for effect in sorted(list(potion.effects), key=ascii_sum, reverse=True):
234                potion.effects[effect](target)247                potion.effects[effect](target)
235248
236    def tick(self):249    def tick(self):
237        for target, potions in self._times.items():250        for target, potions in self._times.items():
238            for potion, time in potions.items():251            for potion, time in potions.items():
239                self._times[target][potion] = time - 1252                self._times[target][potion] = time - 1
240        253        
241        for target, potions in (self._times.copy()).items():254        for target, potions in (self._times.copy()).items():
242            for potion, time in (potions.copy()).items():255            for potion, time in (potions.copy()).items():
n243                if time == 0:n256                if time <= 0:
244                    self._times[target].pop(potion, None)257                    self._times[target].pop(potion, None)
245                    self._target_potions[target].remove(potion)258                    self._target_potions[target].remove(potion)
246                    if len(self._times[target]) == 0:259                    if len(self._times[target]) == 0:
247                        self._times.pop(target, None)260                        self._times.pop(target, None)
248                    self.reset_target(target)261                    self.reset_target(target)
249                    if target not in self._times:262                    if target not in self._times:
250                        self._target_potions.pop(target, None)263                        self._target_potions.pop(target, None)
251                        self._target_states.pop(target, None)264                        self._target_states.pop(target, None)
252265
253    def save_initial_state(self, target):266    def save_initial_state(self, target):
254        self._target_states[target] = {attr: getattr(target, attr) for attr in dir(target) if not attr.startswith('_')}267        self._target_states[target] = {attr: getattr(target, attr) for attr in dir(target) if not attr.startswith('_')}
255    268    
256    def reset_target(self, target):269    def reset_target(self, target):
257        for attr, initial_value in self._target_states[target].items():270        for attr, initial_value in self._target_states[target].items():
258            setattr(target, attr, initial_value)271            setattr(target, attr, initial_value)
259        for attr in dir(target):272        for attr in dir(target):
260            if not attr.startswith('_') and attr not in self._target_states[target].keys():273            if not attr.startswith('_') and attr not in self._target_states[target].keys():
261                try:274                try:
262                    delattr(target, attr)275                    delattr(target, attr)
263                except AttributeError:276                except AttributeError:
264                    continue277                    continue
265278
266        self._apply_all(target)279        self._apply_all(target)
t267 t
268 
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import mathf1import math
22
3class Potion:3class Potion:
4    def __init__(self, effects, duration):4    def __init__(self, effects, duration):
5        self.effects = effects5        self.effects = effects
6        self.duration = duration6        self.duration = duration
7        self.intensities = {}7        self.intensities = {}
8        self.used_effects = set()8        self.used_effects = set()
9        self.used = False9        self.used = False
10        self.depleted = False10        self.depleted = False
1111
12        for name in effects.keys():12        for name in effects.keys():
13            self.intensities[name] = self.intensities.get(name, 0) + 113            self.intensities[name] = self.intensities.get(name, 0) + 1
n14    n14 
15    def __getattr__(self, name):15    def __getattr__(self, name):
nn16        if name not in self.effects.keys():
17            raise AttributeError("Attribute not found.")
16        if self.used:18        if self.used:
17            raise TypeError("Potion is now part of something bigger than itself.")19            raise TypeError("Potion is now part of something bigger than itself.")
n18        elif name in self.used_effects:n20        if name in self.used_effects:
19            raise TypeError("Effect is depleted.")21            raise TypeError("Effect is depleted.")
n20        elif name in self.effects.keys():n22 
21            def execute_n_times(func):23        def execute_n_times(func):
22                def wrapper(target):24            def wrapper(target):
23                    for _ in range(self.intensities.get(name)):25                for _ in range(int(self.intensities.get(name))):
24                        func(target)26                    func(target)
25                return wrapper27            return wrapper
26            self.used_effects.add(name)28        self.used_effects.add(name)
27            if len(self.used_effects) == len(self.effects):29        if len(self.used_effects) == len(self.effects):
28                self.depleted = True30            self.depleted = True
29            return execute_n_times(self.effects.get(name))31        return execute_n_times(self.effects.get(name))
30        else:32 
31            raise AttributeError("Attribute not found")
32        
33    def __add__(self, other):33    def __add__(self, other):
n34        if self.depleted:n34        validate(self)
35            raise TypeError("Potion is depleted.")35        validate(other)
36        if self.used or other.used:
37            raise TypeError("Potion is now part of something bigger than itself.")
3836
n39        new_duration = other.duration if other.duration > self.duration else self.duration n37        new_duration = max(self.duration, other.duration)
40        combined_effects = {}38        combined_effects = {}
41        intensities = {}39        intensities = {}
42        used_effects = set()40        used_effects = set()
4341
44        for name, usage in self.effects.items():42        for name, usage in self.effects.items():
45            combined_effects[name] = usage43            combined_effects[name] = usage
46            intensities[name] = intensities.get(name) + 1 if name in intensities else self.intensities.get(name, 0)44            intensities[name] = intensities.get(name) + 1 if name in intensities else self.intensities.get(name, 0)
47            used_effects = used_effects.union(self.used_effects)45            used_effects = used_effects.union(self.used_effects)
4846
49        for name, usage in other.effects.items():47        for name, usage in other.effects.items():
50            combined_effects[name] = usage48            combined_effects[name] = usage
51            intensities[name] = intensities.get(name) + 1 if name in intensities else other.intensities.get(name, 0)49            intensities[name] = intensities.get(name) + 1 if name in intensities else other.intensities.get(name, 0)
n52            used_effects = used_effects.union(self.used_effects)n50            used_effects = used_effects.union(other.used_effects)
5351
54        self.used = True52        self.used = True
55        other.used = True53        other.used = True
56        potion = Potion(combined_effects, new_duration)54        potion = Potion(combined_effects, new_duration)
57        potion.intensities = intensities55        potion.intensities = intensities
58        potion.used_effects = used_effects56        potion.used_effects = used_effects
59        if len(potion.used_effects) == len(potion.effects):57        if len(potion.used_effects) == len(potion.effects):
nn58            potion.depleted = True
59        return potion
60 
61    def __mul__(self, other):
62        validate(self)
63 
64        if isinstance(other, (int, float)):
65            potion = Potion(self.effects.copy(), self.duration)
66            intensities = {}
67 
68            for name, value in self.intensities.items():
69                intensities[name] = value * other
70                if 0 < other < 1:
71                    intensities[name] = self._round_value(intensities[name])
72 
73            potion.intensities = intensities
74            potion.used_effects = self.used_effects.copy()
75            if len(potion.used_effects) == len(potion.effects):
60                potion.depleted = True76                potion.depleted = True
n61        return potionn
62    
63    def __mul__(self, other):
64        if self.depleted:
65            raise TypeError("Potion is depleted.")
66        if self.used:
67            raise TypeError("Potion is now part of something bigger than itself.")
68 
69        potion = Potion(self.effects, self.duration)
70        intensities = {}
71 
72        if isinstance(other, (int, float)):
73            for name, value in self.intensities.items():
74                    intensities[name] = value * other
75                    if other > 0 and other < 1:
76                        if intensities[name] % 1 > 0.5:
77                            intensities[name]= int(math.ceil(intensities[name]))
78                        else:
79                            intensities[name] = int(math.floor(intensities[name]))
80 
81        potion.intensities = intensities
82        potion.used_effects = self.used_effects.copy()
83        if len(potion.used_effects) == len(potion.effects):
84                potion.depleted = True
85        self.used = True77            self.used = True
86        return potion78            return potion
8779
88    def __sub__(self, other):80    def __sub__(self, other):
n89        if self.depleted:n81        validate(self)
90            raise TypeError("Potion is depleted.")82        validate(other)
91        if self.used or other.used:
92            raise TypeError("Potion is now part of something bigger than itself.")
9383
94        new_duration = other.duration84        new_duration = other.duration
n95        combined_effects = {}n85        purified_effects = {}
96        intensities = {}86        intensities = {}
97        used_effects = set()87        used_effects = set()
9888
99        for name, usage in other.effects.items():89        for name, usage in other.effects.items():
100            if name not in self.effects:90            if name not in self.effects:
n101                raise TypeError("Cannot subtract")n91                raise TypeError("Cannot purify potion.")
102            if other.intensities.get(name) >= self.intensities.get(name):92            if other.intensities.get(name) >= self.intensities.get(name):
103                continue93                continue
104            else:94            else:
n105                combined_effects[name] = usagen95                purified_effects[name] = usage
106                intensities[name] = self.intensities.get(name) - other.intensities.get(name)96                intensities[name] = self.intensities.get(name) - other.intensities.get(name)
n107                used_effects = used_effects.union(other.used_effects)n97                if name in other.used_effects:
98                    used_effects.add(name)
10899
109        for name, usage in self.effects.items():100        for name, usage in self.effects.items():
110            if name not in other.effects.keys():101            if name not in other.effects.keys():
n111                combined_effects[name] = usagen102                purified_effects[name] = usage
112                intensities[name] = self.intensities.get(name)103                intensities[name] = self.intensities.get(name)
n113                used_effects = used_effects.union(self.used_effects)n104                if name in self.used_effects:
105                    used_effects.add(name)
114        106        
115        self.used = True107        self.used = True
116        other.used = True108        other.used = True
n117        potion = Potion(combined_effects, new_duration)n109        potion = Potion(purified_effects, new_duration)
118        potion.intensities = intensities110        potion.intensities = intensities
119        potion.used_effects = used_effects111        potion.used_effects = used_effects
120        if len(potion.used_effects) == len(potion.effects):112        if len(potion.used_effects) == len(potion.effects):
121                potion.depleted = True113                potion.depleted = True
122        return potion114        return potion
n123    n115 
124    def __truediv__(self, other):116    def __truediv__(self, other):
n125        if self.depleted:n117        validate(self)
126            raise TypeError("Potion is depleted.")
127        if self.used:
128            raise TypeError("Potion is now part of something bigger than itself.")
129118
130        if isinstance(other, (int, float)):119        if isinstance(other, (int, float)):
131            intensities = {}120            intensities = {}
132121
133            for name, intensity in self.intensities.items():122            for name, intensity in self.intensities.items():
n134                new_intensity = intensity / othern
135                if other > 0 and other < 1:
136                    if new_intensity % 1 > 0.5:
137                        new_intensity = math.ceil(new_intensity)
138                    else:
139                        new_intensity = math.floor(new_intensity)
140                intensities[name] = int(new_intensity)123                intensities[name] = self._round_value(intensity / other)
141124
142            result = tuple(Potion(self.effects.copy(), self.duration) for _ in range(other))125            result = tuple(Potion(self.effects.copy(), self.duration) for _ in range(other))
143            for potion in result:126            for potion in result:
144                potion.intensities = intensities.copy()127                potion.intensities = intensities.copy()
145                potion.used_effects = self.used_effects.copy()128                potion.used_effects = self.used_effects.copy()
146                if len(potion.used_effects) == len(potion.effects):129                if len(potion.used_effects) == len(potion.effects):
147                    potion.depleted = True130                    potion.depleted = True
nn131            self.used = True
148            return result132            return result
149133
150    134    
151    def __eq__(self, other):135    def __eq__(self, other):
nn136        validate(self)
137        validate(other)
138 
152        if not isinstance(other, Potion):139        if not isinstance(other, Potion):
153            return False140            return False
154141
155        if len(self.effects) != len(other.effects):142        if len(self.effects) != len(other.effects):
156            return False143            return False
157144
158        for name in self.effects.keys():145        for name in self.effects.keys():
159            if name not in other.effects:146            if name not in other.effects:
160                return False147                return False
161            if self.intensities.get(name) != other.intensities.get(name):148            if self.intensities.get(name) != other.intensities.get(name):
162                return False149                return False
163        return True150        return True
164151
165    def __lt__(self, other):152    def __lt__(self, other):
nn153        validate(self)
154        validate(other)
155 
156        if not isinstance(other, Potion):
157            return False
158 
166        first_sum = sum([intensity for intensity in self.intensities.values()])159        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])
167        second_sum = sum([intensity for intensity in other.intensities.values()])160        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])
168        return first_sum < second_sum161        return first_sum < second_sum
169162
170    def __gt__(self, other):163    def __gt__(self, other):
nn164        validate(self)
165        validate(other)
166 
167        if not isinstance(other, Potion):
168            return False
169 
171        first_sum = sum([intensity for intensity in self.intensities.values()])170        first_sum = sum([intensity for name, intensity in self.intensities.items() if name not in self.used_effects])
172        second_sum = sum([intensity for intensity in other.intensities.values()])171        second_sum = sum([intensity for name, intensity in other.intensities.items() if name not in other.used_effects])
173        return first_sum > second_sum172        return first_sum > second_sum
174173
175    def __hash__(self):174    def __hash__(self):
176        return super().__hash__()175        return super().__hash__()
nn176    
177    def _round_value(self, value):
178        if value % 1 > 0.5:
179            return int(math.ceil(value))
180        return int(math.floor(value))
177181
nn182def validate(potion):
183    if potion.depleted:
184        raise TypeError("Potion is depleted.")
185    if potion.used:
186        raise TypeError("Potion is now part of something bigger than itself.")
178187
179class ГоспожатаПоХимия:188class ГоспожатаПоХимия:
180    # {189    # {
181    #   target: { initial_state },190    #   target: { initial_state },
182    #   target2: { initial_state }191    #   target2: { initial_state }
183    # }192    # }
184    _target_states = {}193    _target_states = {}
185194
186    # {195    # {
187    #   target: [potion1, potion2],196    #   target: [potion1, potion2],
188    #   target2: [potion3]197    #   target2: [potion3]
189    # }198    # }
190    _target_potions = {}199    _target_potions = {}
191200
192    # {201    # {
193    #   target: {202    #   target: {
194    #           potion1: time1,203    #           potion1: time1,
195    #           potion2: time2 204    #           potion2: time2 
196    #       },205    #       },
197    #   target2: {206    #   target2: {
198    #           potion3: time3207    #           potion3: time3
199    #       }208    #       }
200    # }209    # }
201    _times = {}210    _times = {}
202211
203    def apply(self, target, potion):212    def apply(self, target, potion):
n204        if potion.depleted:n213        validate(potion)
205            raise TypeError("Potion is depleted.")
206214
207        if target not in self._target_states:215        if target not in self._target_states:
208            self.save_initial_state(target)216            self.save_initial_state(target)
209        if target not in self._times.keys():217        if target not in self._times.keys():
210            self._times[target] = {}218            self._times[target] = {}
211        if target not in self._target_potions.keys():219        if target not in self._target_potions.keys():
212            self._target_potions[target] = []220            self._target_potions[target] = []
213221
214        self._times[target][potion] = potion.duration222        self._times[target][potion] = potion.duration
215        self._target_potions[target].append(potion)223        self._target_potions[target].append(potion)
216        sorted_effects = list(sorted(potion.effects))224        sorted_effects = list(sorted(potion.effects))
217        for effect in sorted_effects:225        for effect in sorted_effects:
218            try:226            try:
219                getattr(potion, effect)(target)227                getattr(potion, effect)(target)
220            except TypeError:228            except TypeError:
n221                passn229                continue
222230
223    def _apply_all(self, target):231    def _apply_all(self, target):
224        for potion in self._target_potions[target]:232        for potion in self._target_potions[target]:
225            for effect in list(sorted(potion.effects)):233            for effect in list(sorted(potion.effects)):
n226                    potion.effects[effect](target)n234                potion.effects[effect](target)
227235
228    def tick(self):236    def tick(self):
229        for target, potions in self._times.items():237        for target, potions in self._times.items():
230            for potion, time in potions.items():238            for potion, time in potions.items():
231                self._times[target][potion] = time - 1239                self._times[target][potion] = time - 1
232        240        
233        for target, potions in (self._times.copy()).items():241        for target, potions in (self._times.copy()).items():
234            for potion, time in (potions.copy()).items():242            for potion, time in (potions.copy()).items():
235                if time == 0:243                if time == 0:
236                    self._times[target].pop(potion, None)244                    self._times[target].pop(potion, None)
237                    self._target_potions[target].remove(potion)245                    self._target_potions[target].remove(potion)
238                    if len(self._times[target]) == 0:246                    if len(self._times[target]) == 0:
239                        self._times.pop(target, None)247                        self._times.pop(target, None)
240                    self.reset_target(target)248                    self.reset_target(target)
241                    if target not in self._times:249                    if target not in self._times:
242                        self._target_potions.pop(target, None)250                        self._target_potions.pop(target, None)
n243 n251                        self._target_states.pop(target, None)
244252
245    def save_initial_state(self, target):253    def save_initial_state(self, target):
246        self._target_states[target] = {attr: getattr(target, attr) for attr in dir(target) if not attr.startswith('_')}254        self._target_states[target] = {attr: getattr(target, attr) for attr in dir(target) if not attr.startswith('_')}
247    255    
248    def reset_target(self, target):256    def reset_target(self, target):
249        for attr, initial_value in self._target_states[target].items():257        for attr, initial_value in self._target_states[target].items():
250            setattr(target, attr, initial_value)258            setattr(target, attr, initial_value)
nn259        for attr in dir(target):
260            if not attr.startswith('_') and attr not in self._target_states[target].keys():
261                try:
262                    delattr(target, attr)
263                except AttributeError:
264                    continue
265 
251        self._apply_all(target)266        self._apply_all(target)
252267
tt268 
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op