1import math as m
2from copy import deepcopy
3
4class Effect:
5 def __init__(self, effect, intensity=1):
6 self.counter = 0
7 self.intensity = intensity
8 self.effect = effect
9
10 def __call__(self, *args, **kwargs):
11 if self.counter >= 1:
12 raise TypeError('Effect is depleted.')
13 else:
14 self.counter += 1
15 for _ in range(self.intensity):
16 self.effect(*args, *kwargs)
17 self.intensity = 0
18
19 def __add__(self, other):
20 return Effect(self.effect, self.intensity + other.intensity)
21
22 def __mul__(self, k):
23 power = self.intensity*k
24 if k % 1:
25 power = m.floor(power) if power % 1 <= 0.5 else round(power)
26 return Effect(self.effect, power)
27
28 def __sub__(self, other):
29 intensity = self.intensity - other.intensity
30 if intensity <= 0:
31 return None
32 return Effect(self.effect, self.intensity - other.intensity)
33
34 def __truediv__(self, k):
35 power = self.intensity/k
36 power = m.floor(power) if power % 1 <= 0.5 else round(power)
37 return Effect(self.effect, power)
38
39
40class Potion:
41 def __init__(self, effects, duration, *args):
42 self.effects = {}
43 for name, func in effects.items():
44 effect = Effect(func)
45 if type(func) != Effect:
46 setattr(self, name, effect)
47 self.effects[name] = effect
48 else:
49 setattr(self, name, func)
50 self.effects[name] = func
51 if args:
52 for name, effect in args[0].items():
53 updated_effect = getattr(self, name) + effect
54 setattr(self, name, updated_effect)
55 self.effects[name] = updated_effect
56 self.duration = duration
57 self.__expired = False
58 self.counter = 0
59
60 def __has_expired(self):
61 if self.counter >= 1:
62 raise TypeError('Potion is depleted.')
63 if self.__expired:
64 raise TypeError('Potion is now part of something bigger than itself.')
65 self.__expired = True
66
67 def __add__(self, other):
68 self.__has_expired()
69 other.__has_expired()
70 return Potion(self.effects|other.effects,
71 max(self.duration, other.duration),
72 {k: e for k, e in self.effects.items() if k in other.effects})
73
74 def __call__(self, *args, **kwargs):
75 self.__has_expired()
76 self.counter += 1
77 def cmp(pair):
78 name, _ = pair
79 return sum(ord(ch) for ch in name)
80 for name , effect in sorted(self.effects.items(), key=cmp, reverse=True):
81 try:
82 effect(*args, **kwargs)
83 except TypeError:
84 continue
85
86 def __mul__(self, intensity):
87 self.__has_expired()
88 return Potion({n: e*intensity for n, e in self.effects.items()}, self.duration)
89
90 def __sub__(self, other):
91 self.__has_expired()
92 other.__has_expired()
93 if len(self.effects) != len(self.effects|other.effects):
94 raise TypeError
95
96 updated_effects = {k: e for k, e in self.effects.items()}
97 for name, effect in other.effects.items():
98 updated_effects[name] -= effect
99 if updated_effects[name] == None:
100 updated_effects.pop(name)
101 return Potion(updated_effects, self.duration)
102
103 def __truediv__(self, n):
104 self.__has_expired()
105 potions = []
106 for _ in range(n):
107 potions.append(Potion({k: e/n for k, e in self.effects.items()}, self.duration))
108 return tuple(potions)
109
110 def __eq__(self, other):
111 for name, effect in self.effects.items():
112 if name in other.effects:
113 if effect.intensity != other.effects[name].intensity:
114 return False
115 else:
116 return False
117 return True
118
119 def __ne__(self, other):
120 return not self == other
121
122 def __gt__(self, other):
123 return sum([e.intensity for e in self.effects.values()]) > sum([e.intensity for e in other.effects.values()])
124
125 def __lt__(self, other):
126 return other > self
127
128
129class ГоспожатаПоХимия:
130 def __init__(self):
131 self.victims = []
132 self.timer = 0
133
134 def apply(self, target, potion):
135 target_copy = deepcopy(target)
136 setattr(target, 'prev', target_copy)
137 potion(target)
138 self.victims.append((target, potion))
139
140 def tick(self):
141 self.timer += 1
142 for victim, potion in self.victims:
143 if potion.duration == self.timer:
144 for attr in [attr for attr in dir(victim.prev) if attr.startswith('__') is False]:
145 setattr(victim, attr, getattr(victim.prev, attr))
...F...F.........F..
======================================================================
FAIL: test_equal (test.TestPotionComparison)
Test equality of potions.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 304, in test_equal
self.assertNotEqual(potion1, potion2)
AssertionError: <solution.Potion object at 0x7fe25f207370> == <solution.Potion object at 0x7fe25f207100>
======================================================================
FAIL: test_deprecation (test.TestPotionOperations)
Test deprecation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 257, in test_deprecation
with self.assertRaisesRegex(TypeError, 'Potion is now part of something bigger than itself\.'):
AssertionError: TypeError not raised
======================================================================
FAIL: test_ticking_multiple_potions (test.TestГоспожатаПоХимия)
Test ticking after applying multiple potions which affect the same attribute.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 471, in test_ticking_multiple_potions
self.assertEqual(self._target.int_attr, 5)
AssertionError: 50 != 5
----------------------------------------------------------------------
Ran 20 tests in 0.002s
FAILED (failures=3)
Виктор Бечев
05.12.2023 13:54Единствената ми забележка са еднобуквените имена. В сравнително прост код и малки функции, както в случая - преживява се, имената са дефинирани или на същия ред или 1-2 реда по-нагоре.
Но е хубава практика да се избягват, много е лесно да го направиш нечетимо, да вкараш колизии на имена и прочие.
|
05.12.2023 13:50