1class Potion:
2
3 def __init__(self, effects, duration):
4 self.effects = effects
5 self.duration = duration
6 self.is_part_of_something_bigger = False
7 self.effects_intensity = {}
8
9 self.set_effects_as_attr()
10 self.set_effects_intensities()
11
12 def set_effects_as_attr(self):
13 for effect_key in self.effects:
14 setattr(self, effect_key, self.effects[effect_key])
15
16 def set_effects_intensities(self):
17 for effect_key in self.effects:
18 self.effects_intensity[effect_key] = 1
19
20 def disable_effect(self, effect_key):
21 self.effects_intensity[effect_key] = -2
22
23 def lower_intensity(self, effect_key):
24 if self.effects_intensity[effect_key] == 0 or self.effects_intensity[effect_key] == 1:
25 self.disable_effect(effect_key)
26 else:
27 self.effects_intensity[effect_key] -= 1
28
29 def filter_repeating_effects(self, other_potion):
30 effects_left = {}
31 for effect_name in self.effects:
32 if effect_name not in other_potion.effects:
33 effects_left[effect_name] = self.effects[effect_name]
34 return effects_left
35
36 def call_methods(self, effect_func, effect):
37 def wrapper(target):
38 for n in range(self.effects_intensity[effect]):
39 effect_func(target)
40 return wrapper
41
42 def __getattr__(self, item):
43 if item in self.effects:
44 if self.effects_intensity[item] < -1:
45 raise TypeError("Effect is depleted.")
46 return self.call_methods(self.effects[item], item)
47 raise AttributeError("Attribute not found")
48
49 def __getattribute__(self, item):
50 if item in object.__getattribute__(self, "effects") and object.__getattribute__(self, "is_part_of_something_bigger"):
51 raise TypeError("Potion is now part of something bigger than itself.")
52 else:
53 return object.__getattribute__(self, item)
54
55 def __add__(self, other):
56 self.is_part_of_something_bigger = True
57 combined_effects = self.effects | other.effects
58 new_duration = self.duration if self.duration > other.duration else other.duration
59 combined_potion = Potion(combined_effects, new_duration)
60 for effect_key in self.effects:
61 if effect_key in other.effects:
62 if self.effects_intensity[effect_key] > 0 and other.effects_intensity[effect_key] > 0:
63 combined_potion.effects_intensity[effect_key] = self.effects_intensity[effect_key] + other.effects_intensity[effect_key]
64 elif not self.effects_intensity[effect_key] and not other.effects_intensity[effect_key]:
65 combined_potion.disable_effect(effect_key)
66 return combined_potion
67
68 def __sub__(self, other):
69 self.is_part_of_something_bigger = True
70 filtered_effects = self.filter_repeating_effects(other)
71 for effect in self.other.effects:
72 if effect not in self.effects:
73 raise TypeError("Removing potion effects which you do not have is illegal !!!")
74 elif self.effects_intensity[effect] > other.effects_intensity[effect]:
75 filtered_effects[effect] = self.effects[effect]
76
77 purified_potion = Potion(filtered_effects, self.duration)
78 for effect in purified_potion.effects:
79 if self.effects_intensity[effect] > other.effects_intensity[effect]:
80 left_intensity = self.effects_intensity[effect] - other.effects_intensity[effect]
81 purified_potion.effects_intensity[effect] = left_intensity
82 else:
83 purified_potion.effects_intensity[effect] = self.effects_intensity[effect]
84 return purified_potion
85
86 def __mul__(self, multi):
87 self.is_part_of_something_bigger = True
88 new_effects_intensity = [round(self.effects_intensity[effect_key] * multi) for effect_key in self.effects]
89 new_potion = Potion(self.effects, self.duration)
90 new_potion.effects_intensity = new_effects_intensity
91 return new_potion
92
93 def __truediv__(self, div):
94 self.is_part_of_something_bigger = True
95 new_potions = ()
96 for n in range(div):
97 diluted_potion = Potion(self.effects, self.duration)
98 diluted_potion.effects_intensity = [round(self.effects_intensity[effect_key] / div) for effect_key in self.effects]
99 new_potions.append(diluted_potion)
100 return new_potions
101
102 def __eq__(self, other):
103 return self.effects == other.effects and self.effects_intensity == other.effects_intensity
104
105 def __gt__(self, other):
106 return sum(self.effects_intensity.values()) > sum(other.effects_intensity.values())
107
108 def __lt__(self, other):
109 return sum(self.effects_intensity.values()) < sum(other.effects_intensity.values())
110
111
112class ГоспожатаПоХимия:
113
114 used_potions = []
115 attacked_targets = []
116
117 def __init__(self):
118 self.magic_pairs = []
119 self.target = None
120 self.unpoisoned_target_data = None
121 self.current_poison_duration = None
122
123 def apply(self, target, potion):
124 self.target = target
125 if self.is_usable(potion):
126 if not self.unpoisoned_target_data and target not in self.attacked_targets:
127 self.unpoisoned_target_data = vars(target)
128 self.attacked_targets.append(target)
129
130 self.current_poison_duration = potion.duration
131 self.sort_potion_effects(potion)
132 for effect, _ in self.magic_pairs:
133 if potion.effects_intensity[effect] >= 0:
134 potion.__getattr__(effect)(target)
135 self.used_potions.append(potion)
136
137 def is_usable(self, potion):
138 if potion not in self.used_potions and not potion.is_part_of_something_bigger:
139 return True
140 raise TypeError("Potion is depleted.")
141
142 def sort_potion_effects(self, potion):
143 for effect in potion.effects:
144 self.magic_pairs.append((effect, self.calculate_molecular_mass(effect)))
145 return self.magic_pairs.sort()
146
147 @staticmethod
148 def calculate_molecular_mass(effect):
149 return sum(ord(c) for c in effect)
150
151 def purify_target(self):
152 possibly_poisoned_target_data = vars(self.target)
153 for attr in self.unpoisoned_target_data:
154 if possibly_poisoned_target_data[attr] != self.unpoisoned_target_data[attr]:
155 possibly_poisoned_target_data[attr] = self.unpoisoned_target_data[attr]
156
157 def tick(self):
158 pass
159
160 def __getattribute__(self, item):
161 if item == "tick":
162 self.current_poison_duration -= 1
163 if self.current_poison_duration <= 0:
164 return object.__getattribute__(self, 'purify_target')
165 return object.__getattribute__(self, item)
.F.FE.FFFFEEFEEEFEEE
======================================================================
ERROR: test_superbness (test.TestPotionComparison)
Test superbness of potions.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 317, in test_superbness
self.assertLess(potion1, potion2)
File "/usr/lib/python3.10/unittest/case.py", line 1230, in assertLess
if not a < b:
File "/tmp/solution.py", line 110, in __lt__
return sum(self.effects_intensity.values()) < sum(other.effects_intensity.values())
AttributeError: 'list' object has no attribute 'values'
======================================================================
ERROR: test_purification (test.TestPotionOperations)
Test purification of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 165, in test_purification
potion = potion1 - potion2
File "/tmp/solution.py", line 72, in __sub__
for effect in self.other.effects:
File "/tmp/solution.py", line 48, in __getattr__
raise AttributeError("Attribute not found")
AttributeError: Attribute not found
======================================================================
ERROR: test_separation (test.TestPotionOperations)
Test separation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 213, in test_separation
potion1, potion2, potion3 = potion / 3
File "/tmp/solution.py", line 99, in __truediv__
diluted_potion.effects_intensity = [round(self.effects_intensity[effect_key] / div) for effect_key in self.effects]
File "/tmp/solution.py", line 99, in <listcomp>
diluted_potion.effects_intensity = [round(self.effects_intensity[effect_key] / div) for effect_key in self.effects]
TypeError: list indices must be integers or slices, not str
======================================================================
ERROR: test_applying_normal_case (test.TestГоспожатаПоХимия)
Test applying a normal potion.
----------------------------------------------------------------------
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 126, in apply
if self.is_usable(potion):
File "/tmp/solution.py", line 141, in is_usable
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/test.py", line 416, in test_applying_order
self._dimitrichka.apply(self._target, potion)
File "/tmp/solution.py", line 134, in apply
if potion.effects_intensity[effect] >= 0:
KeyError: 'aa_name'
======================================================================
ERROR: test_applying_part_of_potion (test.TestГоспожатаПоХимия)
Test applying only a part of a potion.
----------------------------------------------------------------------
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 126, in apply
if self.is_usable(potion):
File "/tmp/solution.py", line 141, in is_usable
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/test.py", line 455, in test_ticking_multiple_potions
self._dimitrichka.apply(self._target, potion2)
File "/tmp/solution.py", line 126, in apply
if self.is_usable(potion):
File "/tmp/solution.py", line 141, in is_usable
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/test.py", line 485, in test_ticking_multiple_targets
self._dimitrichka.apply(target1, potion1)
File "/tmp/solution.py", line 134, in apply
if potion.effects_intensity[effect] >= 0:
TypeError: list indices must be integers or slices, not str
======================================================================
ERROR: test_ticking_mutable (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with mutable attributes.
----------------------------------------------------------------------
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 126, in apply
if self.is_usable(potion):
File "/tmp/solution.py", line 141, in is_usable
raise TypeError("Potion is depleted.")
TypeError: Potion is depleted.
======================================================================
FAIL: test_depletion (test.TestBasicPotion)
Test depletion of a potion effect.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 78, in test_depletion
with self.assertRaisesRegex(TypeError, 'Effect is depleted\.'):
AssertionError: TypeError not raised
======================================================================
FAIL: test_equal (test.TestPotionComparison)
Test equality of potions.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 298, in test_equal
self.assertEqual(potion4, potion3)
AssertionError: <solution.Potion object at 0x7fa7545cb7f0> != <solution.Potion object at 0x7fa7545ca920>
======================================================================
FAIL: test_combination_with_overlap (test.TestPotionOperations)
Test combining potions with overlap.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 105, in test_combination_with_overlap
self.assertEqual(self._target.int_attr, 500)
AssertionError: 50 != 500
======================================================================
FAIL: test_deprecation (test.TestPotionOperations)
Test deprecation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 259, in test_deprecation
with self.assertRaisesRegex(TypeError, 'Potion is now part of something bigger than itself\.'):
AssertionError: TypeError not raised
======================================================================
FAIL: test_dilution (test.TestPotionOperations)
Test dilution of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 120, in test_dilution
self.assertEqual(self._target.int_attr, 5)
AssertionError: 50 != 5
======================================================================
FAIL: test_potentiation (test.TestPotionOperations)
Test potentiation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 112, in test_potentiation
self.assertEqual(self._target.int_attr, 5 * (10 ** 3))
AssertionError: 50 != 5000
======================================================================
FAIL: 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/test.py", line 382, in test_applying_depleted_potion
with self.assertRaisesRegex(TypeError, 'Potion is depleted\.'):
AssertionError: TypeError not raised
======================================================================
FAIL: test_ticking_immutable (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with immutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 429, in test_ticking_immutable
self.assertEqual(self._target.int_attr, 5)
AssertionError: 500 != 5
----------------------------------------------------------------------
Ran 20 tests in 0.002s
FAILED (failures=8, errors=9)
06.12.2023 09:40
06.12.2023 09:41
06.12.2023 09:41
06.12.2023 09:46