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

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

4 точки общо

9 успешни теста
11 неуспешни теста
Код
Скрий всички коментари

  1from copy import deepcopy, copy
  2
  3
  4class Potion:
  5    @staticmethod
  6    def custom_round(number):
  7        if number - int(number) > 0.5:
  8            return int(number) + 1
  9        return int(number)
 10
 11    def __init__(self, effects, duration):
 12        self.effects = effects
 13        self.used_effects = []
 14        self.effects_intensity = {key: 1 for key in effects.keys()}
 15        self.duration = duration
 16        self.used = False
 17
 18    def __getattr__(self, item):
 19        def decorator(func):
 20            def wrapper(target):
 21                for _ in range(self.effects_intensity[list(self.effects.keys())[list(self.effects.values()).index(func)]]):
 22                    func(target)
 23            return wrapper
 24
 25        if self.used:
 26            raise TypeError('Potion is now part of something bigger than itself.')
 27
 28        if item in self.effects.keys():
 29            if item in self.used_effects:
 30                raise TypeError('Effect is depleted.')
 31            self.used_effects.append(item)
 32            return decorator(self.effects[item])
 33        else:
 34            raise AttributeError('Effect not found.')
 35
 36    def __copy__(self):
 37        new_potion = Potion({}, 0)
 38        new_potion.effects = self.effects.copy()
 39        new_potion.used_effects = self.used_effects.copy()
 40        new_potion.effects_intensity = self.effects_intensity.copy()
 41        new_potion.duration = self.duration
 42        new_potion.used = self.used
 43        return new_potion
 44
 45    def __add__(self, other):
 46        if self.used or other.used:
 47            raise TypeError('Potion is now part of something bigger than itself.')
 48
 49        new_potion = self.__copy__()
 50
 51        for key in other.effects.keys():
 52            if key in new_potion.effects_intensity.keys():
 53                new_potion.effects_intensity[key] += other.effects_intensity[key]
 54            else:
 55                new_potion.effects[key] = other.effects[key]
 56                new_potion.effects_intensity[key] = other.effects_intensity[key]
 57
 58        self.used = True
 59        other.used = True
 60
 61        return new_potion
 62
 63    def __mul__(self, num):
 64        if self.used:
 65            raise TypeError('Potion is now part of something bigger than itself.')
 66
 67        if num < 0:
 68            raise ValueError('Multiplier must be non negative.')
 69
 70        new_potion = self.__copy__()
 71        for key in new_potion.effects_intensity.keys():
 72            if 0 < num < 1:
 73                new_potion.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] * num)
 74            else:
 75                new_potion.effects_intensity[key] *= num
 76
 77        self.used = True
 78
 79        return new_potion
 80
 81    def __sub__(self, other):
 82        if self.used or other.used:
 83            raise TypeError('Potion is now part of something bigger than itself.')
 84
 85        if not isinstance(other, Potion):
 86            raise TypeError('Cannot subtract non potion object.')
 87
 88        new_potion = self.__copy__()
 89        for key in other.effects.keys():
 90            if key not in self.effects.keys():
 91                raise TypeError('Cannot subtract potion with different effects.')
 92            if new_potion.effects_intensity[key] - other.effects_intensity[key] <= 0:
 93                del new_potion.effects[key]
 94                del new_potion.effects_intensity[key]
 95            else:
 96                new_potion.effects_intensity[key] -= other.effects_intensity[key]
 97
 98        self.used = True
 99        other.used = True
100
101        return new_potion
102
103    def __truediv__(self, other):
104        if self.used:
105            raise TypeError('Potion is now part of something bigger than itself.')
106
107        if not isinstance(other, int):
108            raise TypeError('Cannot divide non potion object.')
109
110        new_potion = self.__copy__()
111        for key in new_potion.effects_intensity.keys():
112            self.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] / other)
113
114        self.used = True
115
116        return (new_potion,) * other
117
118    def __eq__(self, other):
119        if self.used or other.used:
120            raise TypeError('Potion is now part of something bigger than itself.')
121
122        if not isinstance(other, Potion):
123            raise TypeError('Cannot compare non potion object.')
124
125        return self.effects == other.effects and self.effects_intensity == other.effects_intensity
126
127    def __lt__(self, other):
128        if self.used or other.used:
129            raise TypeError('Potion is now part of something bigger than itself.')
130
131        if not isinstance(other, Potion):
132            raise TypeError('Cannot compare non potion object.')
133
134        lhs_sum = sum(self.effects_intensity.values())
135        rhs_sum = sum(other.effects_intensity.values())
136
137        return lhs_sum < rhs_sum
138
139    def __gt__(self, other):
140        if self.used or other.used:
141            raise TypeError('Potion is now part of something bigger than itself.')
142
143        if not isinstance(other, Potion):
144            raise TypeError('Cannot compare non potion object.')
145
146        lhs_sum = sum(self.effects_intensity.values())
147        rhs_sum = sum(other.effects_intensity.values())
148
149        return lhs_sum > rhs_sum
150
151
152class ГоспожатаПоХимия:
153    def __init__(self):
154        self.ticks = 0
155        self.applied_effects = []
156        self.used_potions = []
157
158    def apply(self, target, potion):
159        def get_moll_mass(name):
160            result = 0
161            for symbol in name:
162                result += ord(symbol)
163            return result
164
165        if potion in self.used_potions:
166            raise TypeError('Potion is depleted.')
167
168        self.used_potions.append(potion)
169
170        if potion.duration == 0:
171            return
172
173        effect_names = list(potion.effects.keys())
174        effect_names.sort(key=lambda x: get_moll_mass(x), reverse=True)
175
176        for key in effect_names:
177            try:
178                state = {attr: val for attr, val in target.__dict__.items() if not attr.startswith('__')}
179                potion.__getattr__(key)(target)
180                for attr in dir(target):
181                    if attr.startswith('__'):
182                        continue
183                    state[attr] = getattr(target, attr) - state[attr]
184                self.applied_effects.append(
185                    (potion.duration, self.ticks, target, state))
186            except TypeError:
187                raise TypeError('Potion is depleted.')
188
189    def tick(self):
190        self.ticks += 1
191
192        self.applied_effects.sort(key=lambda x: x[0])
193        for (duration, ticks_atm, target, obj_before) in self.applied_effects:
194            if self.ticks == ticks_atm + duration:
195                for attr, val in obj_before.items():
196                    cur_value = getattr(target, attr)
197                    setattr(target, attr, cur_value - val)

........E.EFEEEEEEEE
======================================================================
ERROR: test_dilution (test.TestPotionOperations)
Test dilution of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 122, in test_dilution
self._target = copy.deepcopy(self._target)
AttributeError: 'function' object has no attribute 'deepcopy'

======================================================================
ERROR: test_purification (test.TestPotionOperations)
Test purification of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 171, in test_purification
self._target = copy.deepcopy(self._target)
AttributeError: 'function' object has no attribute 'deepcopy'

======================================================================
ERROR: test_applying_depleted_potion (test.TestГоспожатаПоХимия)
Test applying a depleted potion or a potion that was used in a reaction.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 379, in test_applying_depleted_potion
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
ERROR: test_applying_normal_case (test.TestГоспожатаПоХимия)
Test applying a normal potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 350, in test_applying_normal_case
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
ERROR: test_applying_order (test.TestГоспожатаПоХимия)
Test applying order of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 404, in test_applying_order
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
ERROR: test_applying_part_of_potion (test.TestГоспожатаПоХимия)
Test applying only a part of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 365, in test_applying_part_of_potion
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
ERROR: test_ticking_immutable (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with immutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 424, in test_ticking_immutable
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
ERROR: test_ticking_multiple_potions (test.TestГоспожатаПоХимия)
Test ticking after applying multiple potions which affect the same attribute.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 454, in test_ticking_multiple_potions
self._dimitrichka.apply(self._target, potion1)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
ERROR: test_ticking_multiple_targets (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with mutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 485, in test_ticking_multiple_targets
self._dimitrichka.apply(target1, potion1)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
ERROR: test_ticking_mutable (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with mutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/solution.py", line 183, in apply
state[attr] = getattr(target, attr) - state[attr]
TypeError: unsupported operand type(s) for -: 'dict' and 'dict'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/tmp/test.py", line 438, in test_ticking_mutable
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 187, in apply
raise TypeError('Potion is depleted.')
TypeError: Potion is depleted.

======================================================================
FAIL: test_separation (test.TestPotionOperations)
Test separation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 215, in test_separation
self.assertEqual(self._target.int_attr, 5 * (10 ** 3))
AssertionError: 5000000000 != 5000

----------------------------------------------------------------------
Ran 20 tests in 0.002s

FAILED (failures=1, errors=10)

Дискусия
Георги Илиев
05.12.2023 13:11

Със стила не, ама с логиката има много, очаквайте нова версия!
Виктор Бечев
05.12.2023 12:10

Отвъд дребните забележки - не виждам големи проблеми със стила.
История

f1from copy import deepcopy, copyf1from copy import deepcopy, copy
22
33
4class Potion:4class Potion:
5    @staticmethod5    @staticmethod
6    def custom_round(number):6    def custom_round(number):
7        if number - int(number) > 0.5:7        if number - int(number) > 0.5:
8            return int(number) + 18            return int(number) + 1
9        return int(number)9        return int(number)
1010
11    def __init__(self, effects, duration):11    def __init__(self, effects, duration):
12        self.effects = effects12        self.effects = effects
13        self.used_effects = []13        self.used_effects = []
14        self.effects_intensity = {key: 1 for key in effects.keys()}14        self.effects_intensity = {key: 1 for key in effects.keys()}
15        self.duration = duration15        self.duration = duration
16        self.used = False16        self.used = False
1717
18    def __getattr__(self, item):18    def __getattr__(self, item):
19        def decorator(func):19        def decorator(func):
20            def wrapper(target):20            def wrapper(target):
21                for _ in range(self.effects_intensity[list(self.effects.keys())[list(self.effects.values()).index(func)]]):21                for _ in range(self.effects_intensity[list(self.effects.keys())[list(self.effects.values()).index(func)]]):
22                    func(target)22                    func(target)
23            return wrapper23            return wrapper
2424
25        if self.used:25        if self.used:
26            raise TypeError('Potion is now part of something bigger than itself.')26            raise TypeError('Potion is now part of something bigger than itself.')
2727
28        if item in self.effects.keys():28        if item in self.effects.keys():
29            if item in self.used_effects:29            if item in self.used_effects:
30                raise TypeError('Effect is depleted.')30                raise TypeError('Effect is depleted.')
31            self.used_effects.append(item)31            self.used_effects.append(item)
32            return decorator(self.effects[item])32            return decorator(self.effects[item])
33        else:33        else:
34            raise AttributeError('Effect not found.')34            raise AttributeError('Effect not found.')
3535
36    def __copy__(self):36    def __copy__(self):
37        new_potion = Potion({}, 0)37        new_potion = Potion({}, 0)
38        new_potion.effects = self.effects.copy()38        new_potion.effects = self.effects.copy()
39        new_potion.used_effects = self.used_effects.copy()39        new_potion.used_effects = self.used_effects.copy()
40        new_potion.effects_intensity = self.effects_intensity.copy()40        new_potion.effects_intensity = self.effects_intensity.copy()
41        new_potion.duration = self.duration41        new_potion.duration = self.duration
42        new_potion.used = self.used42        new_potion.used = self.used
43        return new_potion43        return new_potion
4444
45    def __add__(self, other):45    def __add__(self, other):
46        if self.used or other.used:46        if self.used or other.used:
47            raise TypeError('Potion is now part of something bigger than itself.')47            raise TypeError('Potion is now part of something bigger than itself.')
4848
49        new_potion = self.__copy__()49        new_potion = self.__copy__()
5050
51        for key in other.effects.keys():51        for key in other.effects.keys():
52            if key in new_potion.effects_intensity.keys():52            if key in new_potion.effects_intensity.keys():
53                new_potion.effects_intensity[key] += other.effects_intensity[key]53                new_potion.effects_intensity[key] += other.effects_intensity[key]
54            else:54            else:
55                new_potion.effects[key] = other.effects[key]55                new_potion.effects[key] = other.effects[key]
56                new_potion.effects_intensity[key] = other.effects_intensity[key]56                new_potion.effects_intensity[key] = other.effects_intensity[key]
5757
58        self.used = True58        self.used = True
59        other.used = True59        other.used = True
6060
61        return new_potion61        return new_potion
6262
63    def __mul__(self, num):63    def __mul__(self, num):
64        if self.used:64        if self.used:
65            raise TypeError('Potion is now part of something bigger than itself.')65            raise TypeError('Potion is now part of something bigger than itself.')
6666
67        if num < 0:67        if num < 0:
68            raise ValueError('Multiplier must be non negative.')68            raise ValueError('Multiplier must be non negative.')
6969
70        new_potion = self.__copy__()70        new_potion = self.__copy__()
71        for key in new_potion.effects_intensity.keys():71        for key in new_potion.effects_intensity.keys():
72            if 0 < num < 1:72            if 0 < num < 1:
73                new_potion.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] * num)73                new_potion.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] * num)
74            else:74            else:
75                new_potion.effects_intensity[key] *= num75                new_potion.effects_intensity[key] *= num
7676
77        self.used = True77        self.used = True
7878
79        return new_potion79        return new_potion
8080
81    def __sub__(self, other):81    def __sub__(self, other):
82        if self.used or other.used:82        if self.used or other.used:
83            raise TypeError('Potion is now part of something bigger than itself.')83            raise TypeError('Potion is now part of something bigger than itself.')
8484
85        if not isinstance(other, Potion):85        if not isinstance(other, Potion):
86            raise TypeError('Cannot subtract non potion object.')86            raise TypeError('Cannot subtract non potion object.')
8787
88        new_potion = self.__copy__()88        new_potion = self.__copy__()
89        for key in other.effects.keys():89        for key in other.effects.keys():
90            if key not in self.effects.keys():90            if key not in self.effects.keys():
91                raise TypeError('Cannot subtract potion with different effects.')91                raise TypeError('Cannot subtract potion with different effects.')
92            if new_potion.effects_intensity[key] - other.effects_intensity[key] <= 0:92            if new_potion.effects_intensity[key] - other.effects_intensity[key] <= 0:
93                del new_potion.effects[key]93                del new_potion.effects[key]
94                del new_potion.effects_intensity[key]94                del new_potion.effects_intensity[key]
95            else:95            else:
96                new_potion.effects_intensity[key] -= other.effects_intensity[key]96                new_potion.effects_intensity[key] -= other.effects_intensity[key]
9797
98        self.used = True98        self.used = True
99        other.used = True99        other.used = True
100100
101        return new_potion101        return new_potion
102102
103    def __truediv__(self, other):103    def __truediv__(self, other):
104        if self.used:104        if self.used:
105            raise TypeError('Potion is now part of something bigger than itself.')105            raise TypeError('Potion is now part of something bigger than itself.')
106106
107        if not isinstance(other, int):107        if not isinstance(other, int):
108            raise TypeError('Cannot divide non potion object.')108            raise TypeError('Cannot divide non potion object.')
109109
110        new_potion = self.__copy__()110        new_potion = self.__copy__()
111        for key in new_potion.effects_intensity.keys():111        for key in new_potion.effects_intensity.keys():
112            self.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] / other)112            self.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] / other)
113113
114        self.used = True114        self.used = True
115115
116        return (new_potion,) * other116        return (new_potion,) * other
117117
118    def __eq__(self, other):118    def __eq__(self, other):
119        if self.used or other.used:119        if self.used or other.used:
120            raise TypeError('Potion is now part of something bigger than itself.')120            raise TypeError('Potion is now part of something bigger than itself.')
121121
122        if not isinstance(other, Potion):122        if not isinstance(other, Potion):
123            raise TypeError('Cannot compare non potion object.')123            raise TypeError('Cannot compare non potion object.')
124124
125        return self.effects == other.effects and self.effects_intensity == other.effects_intensity125        return self.effects == other.effects and self.effects_intensity == other.effects_intensity
126126
127    def __lt__(self, other):127    def __lt__(self, other):
128        if self.used or other.used:128        if self.used or other.used:
129            raise TypeError('Potion is now part of something bigger than itself.')129            raise TypeError('Potion is now part of something bigger than itself.')
130130
131        if not isinstance(other, Potion):131        if not isinstance(other, Potion):
132            raise TypeError('Cannot compare non potion object.')132            raise TypeError('Cannot compare non potion object.')
133133
134        lhs_sum = sum(self.effects_intensity.values())134        lhs_sum = sum(self.effects_intensity.values())
135        rhs_sum = sum(other.effects_intensity.values())135        rhs_sum = sum(other.effects_intensity.values())
136136
137        return lhs_sum < rhs_sum137        return lhs_sum < rhs_sum
138138
139    def __gt__(self, other):139    def __gt__(self, other):
140        if self.used or other.used:140        if self.used or other.used:
141            raise TypeError('Potion is now part of something bigger than itself.')141            raise TypeError('Potion is now part of something bigger than itself.')
142142
143        if not isinstance(other, Potion):143        if not isinstance(other, Potion):
144            raise TypeError('Cannot compare non potion object.')144            raise TypeError('Cannot compare non potion object.')
145145
146        lhs_sum = sum(self.effects_intensity.values())146        lhs_sum = sum(self.effects_intensity.values())
147        rhs_sum = sum(other.effects_intensity.values())147        rhs_sum = sum(other.effects_intensity.values())
148148
149        return lhs_sum > rhs_sum149        return lhs_sum > rhs_sum
150150
151151
152class ГоспожатаПоХимия:152class ГоспожатаПоХимия:
153    def __init__(self):153    def __init__(self):
154        self.ticks = 0154        self.ticks = 0
155        self.applied_effects = []155        self.applied_effects = []
156        self.used_potions = []156        self.used_potions = []
157157
158    def apply(self, target, potion):158    def apply(self, target, potion):
159        def get_moll_mass(name):159        def get_moll_mass(name):
160            result = 0160            result = 0
161            for symbol in name:161            for symbol in name:
162                result += ord(symbol)162                result += ord(symbol)
163            return result163            return result
164164
165        if potion in self.used_potions:165        if potion in self.used_potions:
166            raise TypeError('Potion is depleted.')166            raise TypeError('Potion is depleted.')
167167
168        self.used_potions.append(potion)168        self.used_potions.append(potion)
169169
170        if potion.duration == 0:170        if potion.duration == 0:
171            return171            return
172172
173        effect_names = list(potion.effects.keys())173        effect_names = list(potion.effects.keys())
174        effect_names.sort(key=lambda x: get_moll_mass(x), reverse=True)174        effect_names.sort(key=lambda x: get_moll_mass(x), reverse=True)
175175
176        for key in effect_names:176        for key in effect_names:
177            try:177            try:
n178                state = {(attr, val) for attr, val in target.__dict__.items() if not attr.startswith('__')}n178                state = {attr: val for attr, val in target.__dict__.items() if not attr.startswith('__')}
179                potion.__getattr__(key)(target)179                potion.__getattr__(key)(target)
180                for attr in dir(target):180                for attr in dir(target):
181                    if attr.startswith('__'):181                    if attr.startswith('__'):
182                        continue182                        continue
183                    state[attr] = getattr(target, attr) - state[attr]183                    state[attr] = getattr(target, attr) - state[attr]
184                self.applied_effects.append(184                self.applied_effects.append(
185                    (potion.duration, self.ticks, target, state))185                    (potion.duration, self.ticks, target, state))
186            except TypeError:186            except TypeError:
187                raise TypeError('Potion is depleted.')187                raise TypeError('Potion is depleted.')
188188
189    def tick(self):189    def tick(self):
190        self.ticks += 1190        self.ticks += 1
191191
192        self.applied_effects.sort(key=lambda x: x[0])192        self.applied_effects.sort(key=lambda x: x[0])
193        for (duration, ticks_atm, target, obj_before) in self.applied_effects:193        for (duration, ticks_atm, target, obj_before) in self.applied_effects:
194            if self.ticks == ticks_atm + duration:194            if self.ticks == ticks_atm + duration:
t195                for attr, val in obj_before:t195                for attr, val in obj_before.items():
196                    cur_value = getattr(target, attr)
196                    setattr(target, attr, val * -1)197                    setattr(target, attr, cur_value - val)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

n1from copy import deepcopyn1from copy import deepcopy, copy
22
33
4class Potion:4class Potion:
5    @staticmethod5    @staticmethod
6    def custom_round(number):6    def custom_round(number):
n7        if number - int(number) >= 0.5:n7        if number - int(number) > 0.5:
8            return int(number) + 18            return int(number) + 1
9        return int(number)9        return int(number)
1010
11    def __init__(self, effects, duration):11    def __init__(self, effects, duration):
12        self.effects = effects12        self.effects = effects
13        self.used_effects = []13        self.used_effects = []
14        self.effects_intensity = {key: 1 for key in effects.keys()}14        self.effects_intensity = {key: 1 for key in effects.keys()}
15        self.duration = duration15        self.duration = duration
16        self.used = False16        self.used = False
1717
18    def __getattr__(self, item):18    def __getattr__(self, item):
nn19        def decorator(func):
20            def wrapper(target):
21                for _ in range(self.effects_intensity[list(self.effects.keys())[list(self.effects.values()).index(func)]]):
22                    func(target)
23            return wrapper
24 
19        if self.used:25        if self.used:
20            raise TypeError('Potion is now part of something bigger than itself.')26            raise TypeError('Potion is now part of something bigger than itself.')
2127
22        if item in self.effects.keys():28        if item in self.effects.keys():
23            if item in self.used_effects:29            if item in self.used_effects:
24                raise TypeError('Effect is depleted.')30                raise TypeError('Effect is depleted.')
25            self.used_effects.append(item)31            self.used_effects.append(item)
n26            return self.effects[item]n32            return decorator(self.effects[item])
27        else:33        else:
n28            raise TypeError('Effect not found.')n34            raise AttributeError('Effect not found.')
35 
36    def __copy__(self):
37        new_potion = Potion({}, 0)
38        new_potion.effects = self.effects.copy()
39        new_potion.used_effects = self.used_effects.copy()
40        new_potion.effects_intensity = self.effects_intensity.copy()
41        new_potion.duration = self.duration
42        new_potion.used = self.used
43        return new_potion
2944
30    def __add__(self, other):45    def __add__(self, other):
31        if self.used or other.used:46        if self.used or other.used:
32            raise TypeError('Potion is now part of something bigger than itself.')47            raise TypeError('Potion is now part of something bigger than itself.')
3348
n34        merged_effects = {}n49        new_potion = self.__copy__()
35        effects_intensity = {}
3650
n37        for (key, value) in self.effects.items():n
38            merged_effects[key] = value
39        for (key, value) in other.effects.items():51        for key in other.effects.keys():
40            if key in merged_effects:52            if key in new_potion.effects_intensity.keys():
41                effects_intensity[key] += 153                new_potion.effects_intensity[key] += other.effects_intensity[key]
42            else:54            else:
n43                merged_effects[key] = valuen55                new_potion.effects[key] = other.effects[key]
44 
45        new_potion = Potion(effects=merged_effects, duration=max(self.duration, other.duration))
46        new_potion.effects_intensity = effects_intensity.copy()56                new_potion.effects_intensity[key] = other.effects_intensity[key]
4757
48        self.used = True58        self.used = True
49        other.used = True59        other.used = True
5060
51        return new_potion61        return new_potion
5262
53    def __mul__(self, num):63    def __mul__(self, num):
n54        if self.used or other.used:n64        if self.used:
55            raise TypeError('Potion is now part of something bigger than itself.')65            raise TypeError('Potion is now part of something bigger than itself.')
5666
57        if num < 0:67        if num < 0:
58            raise ValueError('Multiplier must be non negative.')68            raise ValueError('Multiplier must be non negative.')
5969
n60        new_potion = Potion(effects=self.effects, duration=self.duration)n70        new_potion = self.__copy__()
61        for key in new_potion.effects_intensity.keys():71        for key in new_potion.effects_intensity.keys():
n62            if not isinstance(value, int) and 0 < num < 1:n72            if 0 < num < 1:
63                new_potion.effects_intensity[key] = custom_round(new_potion.effects_intensity[key] * num)73                new_potion.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] * num)
64            else:74            else:
n65                raise TypeError('Cannot dissolve with number not between 0 and 1.')n
66            new_potion.effects_intensity[key] *= num75                new_potion.effects_intensity[key] *= num
6776
68        self.used = True77        self.used = True
n69        other.used = Truen
7078
71        return new_potion79        return new_potion
7280
73    def __sub__(self, other):81    def __sub__(self, other):
74        if self.used or other.used:82        if self.used or other.used:
75            raise TypeError('Potion is now part of something bigger than itself.')83            raise TypeError('Potion is now part of something bigger than itself.')
7684
77        if not isinstance(other, Potion):85        if not isinstance(other, Potion):
78            raise TypeError('Cannot subtract non potion object.')86            raise TypeError('Cannot subtract non potion object.')
7987
n80        new_potion = Potion(effects=self.effects, duration=self.duration)n88        new_potion = self.__copy__()
81        for key in other.effects.keys():89        for key in other.effects.keys():
n82            if key not in new_potion.effects.items():n90            if key not in self.effects.keys():
83                raise TypeError('Cannot subtract potion with different effects.')91                raise TypeError('Cannot subtract potion with different effects.')
n84            if new_potion.effects_intensity[key] - other.effects_intensity[key] < 0:n92            if new_potion.effects_intensity[key] - other.effects_intensity[key] <= 0:
85                del new_potion.effects[key]93                del new_potion.effects[key]
nn94                del new_potion.effects_intensity[key]
86            else:95            else:
87                new_potion.effects_intensity[key] -= other.effects_intensity[key]96                new_potion.effects_intensity[key] -= other.effects_intensity[key]
8897
89        self.used = True98        self.used = True
90        other.used = True99        other.used = True
91100
92        return new_potion101        return new_potion
93102
94    def __truediv__(self, other):103    def __truediv__(self, other):
n95        if self.used or other.used:n104        if self.used:
96            raise TypeError('Potion is now part of something bigger than itself.')105            raise TypeError('Potion is now part of something bigger than itself.')
97106
98        if not isinstance(other, int):107        if not isinstance(other, int):
99            raise TypeError('Cannot divide non potion object.')108            raise TypeError('Cannot divide non potion object.')
100109
n101        new_potion = Potion(effects=self.effects, duration=self.duration)n110        new_potion = self.__copy__()
102        for key in new_potion.effects_intensity.keys():111        for key in new_potion.effects_intensity.keys():
n103            effects_intensity[key] = custom_round(new_potion.effects_intensity[key] / other)n112            self.effects_intensity[key] = Potion.custom_round(new_potion.effects_intensity[key] / other)
104113
105        self.used = True114        self.used = True
n106        other.used = Truen
107115
108        return (new_potion,) * other116        return (new_potion,) * other
109117
110    def __eq__(self, other):118    def __eq__(self, other):
111        if self.used or other.used:119        if self.used or other.used:
112            raise TypeError('Potion is now part of something bigger than itself.')120            raise TypeError('Potion is now part of something bigger than itself.')
113121
114        if not isinstance(other, Potion):122        if not isinstance(other, Potion):
115            raise TypeError('Cannot compare non potion object.')123            raise TypeError('Cannot compare non potion object.')
116124
117        return self.effects == other.effects and self.effects_intensity == other.effects_intensity125        return self.effects == other.effects and self.effects_intensity == other.effects_intensity
118126
119    def __lt__(self, other):127    def __lt__(self, other):
120        if self.used or other.used:128        if self.used or other.used:
121            raise TypeError('Potion is now part of something bigger than itself.')129            raise TypeError('Potion is now part of something bigger than itself.')
122130
123        if not isinstance(other, Potion):131        if not isinstance(other, Potion):
124            raise TypeError('Cannot compare non potion object.')132            raise TypeError('Cannot compare non potion object.')
125133
126        lhs_sum = sum(self.effects_intensity.values())134        lhs_sum = sum(self.effects_intensity.values())
127        rhs_sum = sum(other.effects_intensity.values())135        rhs_sum = sum(other.effects_intensity.values())
128136
129        return lhs_sum < rhs_sum137        return lhs_sum < rhs_sum
130138
131    def __gt__(self, other):139    def __gt__(self, other):
132        if self.used or other.used:140        if self.used or other.used:
133            raise TypeError('Potion is now part of something bigger than itself.')141            raise TypeError('Potion is now part of something bigger than itself.')
134142
135        if not isinstance(other, Potion):143        if not isinstance(other, Potion):
136            raise TypeError('Cannot compare non potion object.')144            raise TypeError('Cannot compare non potion object.')
137145
138        lhs_sum = sum(self.effects_intensity.values())146        lhs_sum = sum(self.effects_intensity.values())
139        rhs_sum = sum(other.effects_intensity.values())147        rhs_sum = sum(other.effects_intensity.values())
140148
141        return lhs_sum > rhs_sum149        return lhs_sum > rhs_sum
142150
143151
144class ГоспожатаПоХимия:152class ГоспожатаПоХимия:
145    def __init__(self):153    def __init__(self):
146        self.ticks = 0154        self.ticks = 0
147        self.applied_effects = []155        self.applied_effects = []
148        self.used_potions = []156        self.used_potions = []
149157
150    def apply(self, target, potion):158    def apply(self, target, potion):
151        def get_moll_mass(name):159        def get_moll_mass(name):
152            result = 0160            result = 0
153            for symbol in name:161            for symbol in name:
154                result += ord(symbol)162                result += ord(symbol)
155            return result163            return result
156164
157        if potion in self.used_potions:165        if potion in self.used_potions:
158            raise TypeError('Potion is depleted.')166            raise TypeError('Potion is depleted.')
159167
nn168        self.used_potions.append(potion)
169 
170        if potion.duration == 0:
171            return
172 
160        effect_names = list(potion.effects.keys())173        effect_names = list(potion.effects.keys())
161        effect_names.sort(key=lambda x: get_moll_mass(x), reverse=True)174        effect_names.sort(key=lambda x: get_moll_mass(x), reverse=True)
162175
163        for key in effect_names:176        for key in effect_names:
n164            for _ in range(potion.effects_intensity[key]):n
165                try:177            try:
166                    obj_before = deepcopy(target)178                state = {(attr, val) for attr, val in target.__dict__.items() if not attr.startswith('__')}
167                    potion.__getattr__(key)(target)179                potion.__getattr__(key)(target)
180                for attr in dir(target):
181                    if attr.startswith('__'):
182                        continue
183                    state[attr] = getattr(target, attr) - state[attr]
168                    self.applied_effects.append(184                self.applied_effects.append(
169                        (potion.duration, target, obj_before.__dict__.copy()))185                    (potion.duration, self.ticks, target, state))
170                except TypeError:186            except TypeError:
171                    pass187                raise TypeError('Potion is depleted.')
172 
173        self.used_potions.append(potion)
174188
175    def tick(self):189    def tick(self):
176        self.ticks += 1190        self.ticks += 1
177191
tt192        self.applied_effects.sort(key=lambda x: x[0])
178        for (duration, target, obj_before) in self.applied_effects:193        for (duration, ticks_atm, target, obj_before) in self.applied_effects:
179            if duration == self.ticks:194            if self.ticks == ticks_atm + duration:
180                target.__dict__ = obj_before.copy()195                for attr, val in obj_before:
196                    setattr(target, attr, val * -1)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op