1import math
2from contextlib import suppress
3from collections import deque
4from copy import deepcopy
5
6TRANSCENDED = 'Potion is now part of something bigger than itself.'
7DEPLETED_P = 'Potion is depleted.'
8DEPLETED_E = 'Effect is depleted.'
9
10
11def round_half_down(num):
12 return math.ceil(num - 0.5)
13
14
15def 使ったら消えてしまう(*msgs):
16 def decorator(cls):
17 class UseOnce(cls):
18 def __init__(self, *args, **kwargs):
19 super().__init__(*args, **kwargs)
20 for idx in range(len(msgs)):
21 setattr(self, f'__{idx}', False)
22
23 def __getattribute__(self, item):
24 for idx in range(len(msgs)):
25 if object.__getattribute__(self, f'__{idx}'):
26 raise TypeError(msgs[idx])
27 return super().__getattribute__(item)
28
29 return UseOnce
30
31 return decorator
32
33
34def 消えたら伝えろ(idx):
35 def once(method):
36 def wrapper(self, other):
37 res = method(self, other)
38 setattr(self, f'__{idx}', True)
39 if type(other) is Potion:
40 setattr(other, f'__{idx}', True)
41 return res
42
43 return wrapper
44
45 return once
46
47
48@使ったら消えてしまう(DEPLETED_E)
49class Effect:
50 @staticmethod
51 def mole_mass(effect):
52 return sum(ord(char) for char in effect)
53
54 def __init__(self, effect):
55 self.effect = effect
56 self.intensity = 1
57
58 @消えたら伝えろ(0)
59 def __call__(self, target):
60 while self.intensity > 0:
61 self.effect(target)
62 self.intensity -= 1
63
64
65@使ったら消えてしまう(DEPLETED_P, TRANSCENDED)
66class Potion:
67 def __init__(self, effects, duration):
68 self.effects = effects
69 self.duration = duration
70 for effect_name, effect in effects.items():
71 setattr(self, effect_name, Effect(effect))
72
73 def __getitem__(self, effect):
74 return getattr(self, effect)
75
76 def __setitem__(self, effect_name, effect):
77 self.effects[effect_name] = effect
78 setattr(self, effect_name, Effect(effect))
79
80 def __delitem__(self, key):
81 delattr(self, key)
82 del self.effects[key]
83
84 @消えたら伝えろ(1)
85 def __add__(self, other):
86 potion = Potion({}, max(self.duration, other.duration))
87 potion._copy_unused_effects(self)
88 potion._copy_unused_effects(other)
89 for effect in set(self.effects) & set(other.effects):
90 with suppress(TypeError):
91 potion[effect].intensity = self[effect].intensity + other[effect].intensity
92 return potion
93
94 @消えたら伝えろ(1)
95 def __sub__(self, other):
96 if set(other.effects) - set(self.effects):
97 raise TypeError
98 potion = Potion({}, self.duration)
99 potion._copy_unused_effects(self)
100 for effect in other.effects:
101 with suppress(TypeError):
102 intensity = self[effect].intensity - other[effect].intensity
103 if intensity <= 0:
104 del potion[effect]
105 else:
106 potion[effect].intensity = intensity
107 return potion
108
109 @消えたら伝えろ(1)
110 def __mul__(self, other):
111 potion = Potion({}, self.duration)
112 potion._copy_unused_effects(self)
113 for effect in self.effects:
114 with suppress(TypeError):
115 potion[effect].intensity = round_half_down(self[effect].intensity * other)
116 return potion
117
118 @消えたら伝えろ(1)
119 def __truediv__(self, other):
120 potions = []
121 for _ in range(other):
122 potion = Potion({}, round_half_down(self.duration / other))
123 potion._copy_unused_effects(self)
124 for effect in self.effects:
125 with suppress(TypeError):
126 potion[effect].intensity = round_half_down(self[effect].intensity / other)
127 potions.append(potion)
128 return potions
129
130 def __eq__(self, other):
131 if self.effects.keys() != other.effects.keys():
132 return False
133 for effect in self.effects:
134 intensity_1, intensity_2 = 0, 0
135 with suppress(TypeError):
136 intensity_1 = self[effect].intensity
137 with suppress(TypeError):
138 intensity_2 = other[effect].intensity
139 if intensity_1 != intensity_2:
140 return False
141 return True
142
143 def __gt__(self, other):
144 return self._total_intensities() > other._total_intensities()
145
146 @消えたら伝えろ(0)
147 def __call__(self, target):
148 for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass):
149 with suppress(TypeError):
150 self[effect](target)
151
152 def __neg__(self):
153 potion = Potion({}, self.duration - 1)
154 potion._copy_unused_effects(self)
155 return potion
156
157 def _total_intensities(self):
158 total = 0
159 for effect in self.effects:
160 with suppress(TypeError):
161 total += self[effect].intensity
162 return total
163
164 def _copy_unused_effects(self, other):
165 for effect in other.effects:
166 with suppress(TypeError):
167 self._add_effect(effect, other.effects[effect], other[effect].intensity)
168
169 def _add_effect(self, effect_name, effect, intensity=1):
170 self[effect_name] = effect
171 self[effect_name].intensity = intensity
172
173
174class ГоспожатаПоХимия:
175 def __init__(self):
176 self.addicts = {}
177 self.drugs = {}
178
179 def apply(self, target, potion):
180 if potion.duration:
181 addict_id = self._make_addict(target)
182 self._add_drug(addict_id, -potion)
183 potion(target)
184
185 def tick(self):
186 for addict in list(self.addicts):
187 self._withdraw(addict)
188
189 if not self.drugs[addict]:
190 self._remove(addict)
191 continue
192
193 self._give_drugs(addict)
194
195 def _make_addict(self, target):
196 if id(target) not in self.addicts:
197 self.addicts[id(target)] = target, deepcopy(target.__dict__)
198 self.drugs[id(target)] = deque()
199 return id(target)
200
201 def _add_drug(self, addict_id, potion):
202 if potion.duration > 0:
203 self.drugs[addict_id].append(potion)
204
205 def _withdraw(self, addict_id):
206 self.addicts[addict_id][0].__dict__ = deepcopy(self.addicts[addict_id][1])
207
208 def _give_drugs(self, addict_id):
209 drugs = self.drugs[addict_id]
210 addict = self.addicts[addict_id][0]
211 drugs_to_take = len(drugs)
212
213 while drugs_to_take > 0:
214 drug = drugs.popleft()
215 self._add_drug(addict_id, -drug)
216 drug(addict)
217 drugs_to_take -= 1
218
219 def _remove(self, addict_id):
220 del self.addicts[addict_id]
221 del self.drugs[addict_id]
....................
----------------------------------------------------------------------
Ran 20 tests in 0.004s
OK
Виктор Бечев
04.12.2023 22:50お前はもう死んでいる ? :grin:
|
|
Костадин Русалов
04.12.2023 20:33Имената на декораторите са най-трудни за измисляне. Бяха и на японски по едно време
|
Виктор Бечев
04.12.2023 19:40Отвъд това, не ми прави впечатление нещо стилистично, което да ти дам като съвет.
Виж, за дизайна на декораторите *(не казвам да ги променяш сега, а просто feedback)* - малко е пипкава употребата на индексите. На практика трябва да знаеш какви съобщения си подал на кои индекси в първия декоратор за да можеш да използваш с правилния аргумент втория.
Тук върши работа, но ако имаше 6-7 различни съобщения щеше да е доста по-неприятно.
|
Виктор Бечев
04.12.2023 19:26Не мога да преценя дали имената на декораторите ти ме кефят или ме подлудяват. 😅
|
Георги Кунчев
04.12.2023 08:53- да
- не
- не
|
|
Костадин Русалов
04.12.2023 08:44Възможни ли са следните ситуации:
- една димитричка да приложи отвари върху няколко таргета и да ги следи всички;
- върху един таргет да се приложат отвари от повече от една димитрички;
- да се прилагат странични отвари (директно, не с `.apply`) върху таргет, който се следи от димитричка?
|
Георги Кунчев
03.12.2023 21:01Не е напълно стриктно спрямо условието ни, но за мен най-логичното е следното:
Използван ефект в отвара вече има интензитет нула. При изваждане би било ок и няма да получиш изключение, но тъй като резултиращият интензитет е отрицателно число, този ефект не присъства в резултата.
|
|
Костадин Русалов
03.12.2023 17:51A за пречистването, ако в първата отвара има `grow` и го използваме, преди да пречистим с втора, в която има друг `grow`, `TypeError` ли се възбужда?
|
Георги Кунчев
03.12.2023 17:26На въпрос 1 - да.
За втори въпрос - не. Ако ефектите са използвани поотделно, отварата е все още валидна и може да се прилага или комбинира с друга, просто всичките и ефекти са използвани и отварата не прави нищо.
|
|
Костадин Русалов
03.12.2023 17:12Ако имаме отварa с няколко ефекта, например `grow`, `heal`, `immune` (всички с интензитет 1) и използваме `heal` и `immune`, a след това я комбинираме с друга отвара, която има друг `heal` с интензитет 1, комбинираната би трябвало да има само `grow` и `heal` с интензитети 1, нали?
А ако бяхме използвали от първата всички ефекти поотделно, тогава няма как да комбинираме, защото ще се възбуди `TypeError('Potion is depleted.')`, така ли?
|
Георги Кунчев
03.12.2023 10:48Некоректност на примера е това. След потенцииране не трябва да може да ае сравнява.
|
|
Костадин Русалов
03.12.2023 09:08В примера с оценяването на отвари `grow_potion` се потенцира, което значи, че става неизползвама, но все пак може да се сравнява. Това означава ли, че преходността не важи при сравнение?
|
Георги Кунчев
02.12.2023 21:40С бектикове. Там, където е буквата Ч.
Повече инфо: [тук](/info/showdown)
|
|
Костадин Русалов
02.12.2023 21:37Също да питам как се форматира код тук? 😅
|
Георги Кунчев
02.12.2023 21:34Не съм сигурен какво целиш, но ако направиш декоратор, който е клас, би следвало да можеш да пазиш всичко.
Да, това не сме го застъпвали в лекциите, но можеш. Използвай init и call дъндърите на клас и пак изграждаш декоратор.
|
|
Костадин Русалов
02.12.2023 21:31Качвам това решение само за да отворя дискусия. Искам да попитам дали има как като правя декоратор да взема всички атрибути на декорираната функция, но не като копия, а като референции. Прочетох за `@wraps`, но не ми върши работа, защото се получава следното:
```
def decorator(func):
@wraps(func)
def wrapper():
return func()
return wrapper
def func(): pass
func.attr = 1
original = func
func = decorator(func)
func.attr = 2
assert func.attr == original.attr # тук фейлва
```
Има ли как да стане, или трябва сам да си напиша декоратор подобен на wraps
|
| f | 1 | import math | f | 1 | import math |
| 2 | from contextlib import suppress | 2 | from contextlib import suppress | ||
| 3 | from collections import deque | 3 | from collections import deque | ||
| 4 | from copy import deepcopy | 4 | from copy import deepcopy | ||
| 5 | 5 | ||||
| 6 | TRANSCENDED = 'Potion is now part of something bigger than itself.' | 6 | TRANSCENDED = 'Potion is now part of something bigger than itself.' | ||
| 7 | DEPLETED_P = 'Potion is depleted.' | 7 | DEPLETED_P = 'Potion is depleted.' | ||
| 8 | DEPLETED_E = 'Effect is depleted.' | 8 | DEPLETED_E = 'Effect is depleted.' | ||
| 9 | 9 | ||||
| 10 | 10 | ||||
| 11 | def round_half_down(num): | 11 | def round_half_down(num): | ||
| 12 | return math.ceil(num - 0.5) | 12 | return math.ceil(num - 0.5) | ||
| 13 | 13 | ||||
| 14 | 14 | ||||
| 15 | def 使ったら消えてしまう(*msgs): | 15 | def 使ったら消えてしまう(*msgs): | ||
| 16 | def decorator(cls): | 16 | def decorator(cls): | ||
| 17 | class UseOnce(cls): | 17 | class UseOnce(cls): | ||
| 18 | def __init__(self, *args, **kwargs): | 18 | def __init__(self, *args, **kwargs): | ||
| 19 | super().__init__(*args, **kwargs) | 19 | super().__init__(*args, **kwargs) | ||
| 20 | for idx in range(len(msgs)): | 20 | for idx in range(len(msgs)): | ||
| 21 | setattr(self, f'__{idx}', False) | 21 | setattr(self, f'__{idx}', False) | ||
| 22 | 22 | ||||
| 23 | def __getattribute__(self, item): | 23 | def __getattribute__(self, item): | ||
| 24 | for idx in range(len(msgs)): | 24 | for idx in range(len(msgs)): | ||
| 25 | if object.__getattribute__(self, f'__{idx}'): | 25 | if object.__getattribute__(self, f'__{idx}'): | ||
| 26 | raise TypeError(msgs[idx]) | 26 | raise TypeError(msgs[idx]) | ||
| 27 | return super().__getattribute__(item) | 27 | return super().__getattribute__(item) | ||
| 28 | 28 | ||||
| 29 | return UseOnce | 29 | return UseOnce | ||
| 30 | 30 | ||||
| 31 | return decorator | 31 | return decorator | ||
| 32 | 32 | ||||
| 33 | 33 | ||||
| 34 | def 消えたら伝えろ(idx): | 34 | def 消えたら伝えろ(idx): | ||
| 35 | def once(method): | 35 | def once(method): | ||
| 36 | def wrapper(self, other): | 36 | def wrapper(self, other): | ||
| 37 | res = method(self, other) | 37 | res = method(self, other) | ||
| 38 | setattr(self, f'__{idx}', True) | 38 | setattr(self, f'__{idx}', True) | ||
| 39 | if type(other) is Potion: | 39 | if type(other) is Potion: | ||
| 40 | setattr(other, f'__{idx}', True) | 40 | setattr(other, f'__{idx}', True) | ||
| 41 | return res | 41 | return res | ||
| 42 | 42 | ||||
| 43 | return wrapper | 43 | return wrapper | ||
| 44 | 44 | ||||
| 45 | return once | 45 | return once | ||
| 46 | 46 | ||||
| 47 | 47 | ||||
| 48 | @使ったら消えてしまう(DEPLETED_E) | 48 | @使ったら消えてしまう(DEPLETED_E) | ||
| 49 | class Effect: | 49 | class Effect: | ||
| 50 | @staticmethod | 50 | @staticmethod | ||
| 51 | def mole_mass(effect): | 51 | def mole_mass(effect): | ||
| 52 | return sum(ord(char) for char in effect) | 52 | return sum(ord(char) for char in effect) | ||
| 53 | 53 | ||||
| 54 | def __init__(self, effect): | 54 | def __init__(self, effect): | ||
| 55 | self.effect = effect | 55 | self.effect = effect | ||
| 56 | self.intensity = 1 | 56 | self.intensity = 1 | ||
| 57 | 57 | ||||
| 58 | @消えたら伝えろ(0) | 58 | @消えたら伝えろ(0) | ||
| 59 | def __call__(self, target): | 59 | def __call__(self, target): | ||
| 60 | while self.intensity > 0: | 60 | while self.intensity > 0: | ||
| 61 | self.effect(target) | 61 | self.effect(target) | ||
| 62 | self.intensity -= 1 | 62 | self.intensity -= 1 | ||
| 63 | 63 | ||||
| 64 | 64 | ||||
| 65 | @使ったら消えてしまう(DEPLETED_P, TRANSCENDED) | 65 | @使ったら消えてしまう(DEPLETED_P, TRANSCENDED) | ||
| 66 | class Potion: | 66 | class Potion: | ||
| 67 | def __init__(self, effects, duration): | 67 | def __init__(self, effects, duration): | ||
| 68 | self.effects = effects | 68 | self.effects = effects | ||
| 69 | self.duration = duration | 69 | self.duration = duration | ||
| 70 | for effect_name, effect in effects.items(): | 70 | for effect_name, effect in effects.items(): | ||
| 71 | setattr(self, effect_name, Effect(effect)) | 71 | setattr(self, effect_name, Effect(effect)) | ||
| 72 | 72 | ||||
| 73 | def __getitem__(self, effect): | 73 | def __getitem__(self, effect): | ||
| 74 | return getattr(self, effect) | 74 | return getattr(self, effect) | ||
| 75 | 75 | ||||
| 76 | def __setitem__(self, effect_name, effect): | 76 | def __setitem__(self, effect_name, effect): | ||
| 77 | self.effects[effect_name] = effect | 77 | self.effects[effect_name] = effect | ||
| 78 | setattr(self, effect_name, Effect(effect)) | 78 | setattr(self, effect_name, Effect(effect)) | ||
| 79 | 79 | ||||
| 80 | def __delitem__(self, key): | 80 | def __delitem__(self, key): | ||
| 81 | delattr(self, key) | 81 | delattr(self, key) | ||
| 82 | del self.effects[key] | 82 | del self.effects[key] | ||
| 83 | 83 | ||||
| 84 | @消えたら伝えろ(1) | 84 | @消えたら伝えろ(1) | ||
| 85 | def __add__(self, other): | 85 | def __add__(self, other): | ||
| 86 | potion = Potion({}, max(self.duration, other.duration)) | 86 | potion = Potion({}, max(self.duration, other.duration)) | ||
| 87 | potion._copy_unused_effects(self) | 87 | potion._copy_unused_effects(self) | ||
| 88 | potion._copy_unused_effects(other) | 88 | potion._copy_unused_effects(other) | ||
| 89 | for effect in set(self.effects) & set(other.effects): | 89 | for effect in set(self.effects) & set(other.effects): | ||
| 90 | with suppress(TypeError): | 90 | with suppress(TypeError): | ||
| 91 | potion[effect].intensity = self[effect].intensity + other[effect].intensity | 91 | potion[effect].intensity = self[effect].intensity + other[effect].intensity | ||
| 92 | return potion | 92 | return potion | ||
| 93 | 93 | ||||
| 94 | @消えたら伝えろ(1) | 94 | @消えたら伝えろ(1) | ||
| 95 | def __sub__(self, other): | 95 | def __sub__(self, other): | ||
| 96 | if set(other.effects) - set(self.effects): | 96 | if set(other.effects) - set(self.effects): | ||
| 97 | raise TypeError | 97 | raise TypeError | ||
| 98 | potion = Potion({}, self.duration) | 98 | potion = Potion({}, self.duration) | ||
| n | 99 | potion._copy_unused_effects(self) # do we need it? | n | 99 | potion._copy_unused_effects(self) |
| 100 | for effect in other.effects: | 100 | for effect in other.effects: | ||
| 101 | with suppress(TypeError): | 101 | with suppress(TypeError): | ||
| 102 | intensity = self[effect].intensity - other[effect].intensity | 102 | intensity = self[effect].intensity - other[effect].intensity | ||
| 103 | if intensity <= 0: | 103 | if intensity <= 0: | ||
| 104 | del potion[effect] | 104 | del potion[effect] | ||
| 105 | else: | 105 | else: | ||
| 106 | potion[effect].intensity = intensity | 106 | potion[effect].intensity = intensity | ||
| 107 | return potion | 107 | return potion | ||
| 108 | 108 | ||||
| 109 | @消えたら伝えろ(1) | 109 | @消えたら伝えろ(1) | ||
| 110 | def __mul__(self, other): | 110 | def __mul__(self, other): | ||
| 111 | potion = Potion({}, self.duration) | 111 | potion = Potion({}, self.duration) | ||
| 112 | potion._copy_unused_effects(self) | 112 | potion._copy_unused_effects(self) | ||
| 113 | for effect in self.effects: | 113 | for effect in self.effects: | ||
| 114 | with suppress(TypeError): | 114 | with suppress(TypeError): | ||
| 115 | potion[effect].intensity = round_half_down(self[effect].intensity * other) | 115 | potion[effect].intensity = round_half_down(self[effect].intensity * other) | ||
| 116 | return potion | 116 | return potion | ||
| 117 | 117 | ||||
| 118 | @消えたら伝えろ(1) | 118 | @消えたら伝えろ(1) | ||
| 119 | def __truediv__(self, other): | 119 | def __truediv__(self, other): | ||
| 120 | potions = [] | 120 | potions = [] | ||
| 121 | for _ in range(other): | 121 | for _ in range(other): | ||
| 122 | potion = Potion({}, round_half_down(self.duration / other)) | 122 | potion = Potion({}, round_half_down(self.duration / other)) | ||
| 123 | potion._copy_unused_effects(self) | 123 | potion._copy_unused_effects(self) | ||
| 124 | for effect in self.effects: | 124 | for effect in self.effects: | ||
| 125 | with suppress(TypeError): | 125 | with suppress(TypeError): | ||
| 126 | potion[effect].intensity = round_half_down(self[effect].intensity / other) | 126 | potion[effect].intensity = round_half_down(self[effect].intensity / other) | ||
| 127 | potions.append(potion) | 127 | potions.append(potion) | ||
| 128 | return potions | 128 | return potions | ||
| 129 | 129 | ||||
| 130 | def __eq__(self, other): | 130 | def __eq__(self, other): | ||
| 131 | if self.effects.keys() != other.effects.keys(): | 131 | if self.effects.keys() != other.effects.keys(): | ||
| 132 | return False | 132 | return False | ||
| 133 | for effect in self.effects: | 133 | for effect in self.effects: | ||
| 134 | intensity_1, intensity_2 = 0, 0 | 134 | intensity_1, intensity_2 = 0, 0 | ||
| 135 | with suppress(TypeError): | 135 | with suppress(TypeError): | ||
| 136 | intensity_1 = self[effect].intensity | 136 | intensity_1 = self[effect].intensity | ||
| 137 | with suppress(TypeError): | 137 | with suppress(TypeError): | ||
| 138 | intensity_2 = other[effect].intensity | 138 | intensity_2 = other[effect].intensity | ||
| 139 | if intensity_1 != intensity_2: | 139 | if intensity_1 != intensity_2: | ||
| 140 | return False | 140 | return False | ||
| 141 | return True | 141 | return True | ||
| 142 | 142 | ||||
| 143 | def __gt__(self, other): | 143 | def __gt__(self, other): | ||
| 144 | return self._total_intensities() > other._total_intensities() | 144 | return self._total_intensities() > other._total_intensities() | ||
| 145 | 145 | ||||
| 146 | @消えたら伝えろ(0) | 146 | @消えたら伝えろ(0) | ||
| 147 | def __call__(self, target): | 147 | def __call__(self, target): | ||
| 148 | for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass): | 148 | for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass): | ||
| 149 | with suppress(TypeError): | 149 | with suppress(TypeError): | ||
| 150 | self[effect](target) | 150 | self[effect](target) | ||
| 151 | 151 | ||||
| 152 | def __neg__(self): | 152 | def __neg__(self): | ||
| 153 | potion = Potion({}, self.duration - 1) | 153 | potion = Potion({}, self.duration - 1) | ||
| 154 | potion._copy_unused_effects(self) | 154 | potion._copy_unused_effects(self) | ||
| 155 | return potion | 155 | return potion | ||
| 156 | 156 | ||||
| 157 | def _total_intensities(self): | 157 | def _total_intensities(self): | ||
| 158 | total = 0 | 158 | total = 0 | ||
| 159 | for effect in self.effects: | 159 | for effect in self.effects: | ||
| 160 | with suppress(TypeError): | 160 | with suppress(TypeError): | ||
| 161 | total += self[effect].intensity | 161 | total += self[effect].intensity | ||
| 162 | return total | 162 | return total | ||
| 163 | 163 | ||||
| 164 | def _copy_unused_effects(self, other): | 164 | def _copy_unused_effects(self, other): | ||
| 165 | for effect in other.effects: | 165 | for effect in other.effects: | ||
| 166 | with suppress(TypeError): | 166 | with suppress(TypeError): | ||
| 167 | self._add_effect(effect, other.effects[effect], other[effect].intensity) | 167 | self._add_effect(effect, other.effects[effect], other[effect].intensity) | ||
| 168 | 168 | ||||
| 169 | def _add_effect(self, effect_name, effect, intensity=1): | 169 | def _add_effect(self, effect_name, effect, intensity=1): | ||
| 170 | self[effect_name] = effect | 170 | self[effect_name] = effect | ||
| 171 | self[effect_name].intensity = intensity | 171 | self[effect_name].intensity = intensity | ||
| 172 | 172 | ||||
| 173 | 173 | ||||
| t | 174 | class Addict: | t | ||
| 175 | def __init__(self, addict, potion): | ||||
| 176 | self.addict = addict | ||||
| 177 | self.potion = potion | ||||
| 178 | self.current = addict.__dict__ | ||||
| 179 | self.changed = {} | ||||
| 180 | self.added = set() | ||||
| 181 | |||||
| 182 | def __repr__(self): | ||||
| 183 | return f'{id(self.addict)} with state {self.current}' | ||||
| 184 | |||||
| 185 | |||||
| 186 | class ГоспожатаПоХимия: | 174 | class ГоспожатаПоХимия: | ||
| 187 | def __init__(self): | 175 | def __init__(self): | ||
| 188 | self.addicts = {} | 176 | self.addicts = {} | ||
| 189 | self.drugs = {} | 177 | self.drugs = {} | ||
| 190 | 178 | ||||
| 191 | def apply(self, target, potion): | 179 | def apply(self, target, potion): | ||
| 192 | if potion.duration: | 180 | if potion.duration: | ||
| 193 | addict_id = self._make_addict(target) | 181 | addict_id = self._make_addict(target) | ||
| 194 | self._add_drug(addict_id, -potion) | 182 | self._add_drug(addict_id, -potion) | ||
| 195 | potion(target) | 183 | potion(target) | ||
| 196 | 184 | ||||
| 197 | def tick(self): | 185 | def tick(self): | ||
| 198 | for addict in list(self.addicts): | 186 | for addict in list(self.addicts): | ||
| 199 | self._withdraw(addict) | 187 | self._withdraw(addict) | ||
| 200 | 188 | ||||
| 201 | if not self.drugs[addict]: | 189 | if not self.drugs[addict]: | ||
| 202 | self._remove(addict) | 190 | self._remove(addict) | ||
| 203 | continue | 191 | continue | ||
| 204 | 192 | ||||
| 205 | self._give_drugs(addict) | 193 | self._give_drugs(addict) | ||
| 206 | 194 | ||||
| 207 | def _make_addict(self, target): | 195 | def _make_addict(self, target): | ||
| 208 | if id(target) not in self.addicts: | 196 | if id(target) not in self.addicts: | ||
| 209 | self.addicts[id(target)] = target, deepcopy(target.__dict__) | 197 | self.addicts[id(target)] = target, deepcopy(target.__dict__) | ||
| 210 | self.drugs[id(target)] = deque() | 198 | self.drugs[id(target)] = deque() | ||
| 211 | return id(target) | 199 | return id(target) | ||
| 212 | 200 | ||||
| 213 | def _add_drug(self, addict_id, potion): | 201 | def _add_drug(self, addict_id, potion): | ||
| 214 | if potion.duration > 0: | 202 | if potion.duration > 0: | ||
| 215 | self.drugs[addict_id].append(potion) | 203 | self.drugs[addict_id].append(potion) | ||
| 216 | 204 | ||||
| 217 | def _withdraw(self, addict_id): | 205 | def _withdraw(self, addict_id): | ||
| 218 | self.addicts[addict_id][0].__dict__ = deepcopy(self.addicts[addict_id][1]) | 206 | self.addicts[addict_id][0].__dict__ = deepcopy(self.addicts[addict_id][1]) | ||
| 219 | 207 | ||||
| 220 | def _give_drugs(self, addict_id): | 208 | def _give_drugs(self, addict_id): | ||
| 221 | drugs = self.drugs[addict_id] | 209 | drugs = self.drugs[addict_id] | ||
| 222 | addict = self.addicts[addict_id][0] | 210 | addict = self.addicts[addict_id][0] | ||
| 223 | drugs_to_take = len(drugs) | 211 | drugs_to_take = len(drugs) | ||
| 224 | 212 | ||||
| 225 | while drugs_to_take > 0: | 213 | while drugs_to_take > 0: | ||
| 226 | drug = drugs.popleft() | 214 | drug = drugs.popleft() | ||
| 227 | self._add_drug(addict_id, -drug) | 215 | self._add_drug(addict_id, -drug) | ||
| 228 | drug(addict) | 216 | drug(addict) | ||
| 229 | drugs_to_take -= 1 | 217 | drugs_to_take -= 1 | ||
| 230 | 218 | ||||
| 231 | def _remove(self, addict_id): | 219 | def _remove(self, addict_id): | ||
| 232 | del self.addicts[addict_id] | 220 | del self.addicts[addict_id] | ||
| 233 | del self.drugs[addict_id] | 221 | del self.drugs[addict_id] |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import math | f | 1 | import math |
| 2 | from contextlib import suppress | 2 | from contextlib import suppress | ||
| n | n | 3 | from collections import deque | ||
| 4 | from copy import deepcopy | ||||
| 3 | 5 | ||||
| 4 | TRANSCENDED = 'Potion is now part of something bigger than itself.' | 6 | TRANSCENDED = 'Potion is now part of something bigger than itself.' | ||
| 5 | DEPLETED_P = 'Potion is depleted.' | 7 | DEPLETED_P = 'Potion is depleted.' | ||
| 6 | DEPLETED_E = 'Effect is depleted.' | 8 | DEPLETED_E = 'Effect is depleted.' | ||
| 7 | 9 | ||||
| 8 | 10 | ||||
| 9 | def round_half_down(num): | 11 | def round_half_down(num): | ||
| 10 | return math.ceil(num - 0.5) | 12 | return math.ceil(num - 0.5) | ||
| 11 | 13 | ||||
| 12 | 14 | ||||
| 13 | def 使ったら消えてしまう(*msgs): | 15 | def 使ったら消えてしまう(*msgs): | ||
| 14 | def decorator(cls): | 16 | def decorator(cls): | ||
| 15 | class UseOnce(cls): | 17 | class UseOnce(cls): | ||
| 16 | def __init__(self, *args, **kwargs): | 18 | def __init__(self, *args, **kwargs): | ||
| 17 | super().__init__(*args, **kwargs) | 19 | super().__init__(*args, **kwargs) | ||
| 18 | for idx in range(len(msgs)): | 20 | for idx in range(len(msgs)): | ||
| 19 | setattr(self, f'__{idx}', False) | 21 | setattr(self, f'__{idx}', False) | ||
| 20 | 22 | ||||
| 21 | def __getattribute__(self, item): | 23 | def __getattribute__(self, item): | ||
| 22 | for idx in range(len(msgs)): | 24 | for idx in range(len(msgs)): | ||
| 23 | if object.__getattribute__(self, f'__{idx}'): | 25 | if object.__getattribute__(self, f'__{idx}'): | ||
| 24 | raise TypeError(msgs[idx]) | 26 | raise TypeError(msgs[idx]) | ||
| 25 | return super().__getattribute__(item) | 27 | return super().__getattribute__(item) | ||
| 26 | 28 | ||||
| 27 | return UseOnce | 29 | return UseOnce | ||
| 28 | 30 | ||||
| 29 | return decorator | 31 | return decorator | ||
| 30 | 32 | ||||
| 31 | 33 | ||||
| 32 | def 消えたら伝えろ(idx): | 34 | def 消えたら伝えろ(idx): | ||
| 33 | def once(method): | 35 | def once(method): | ||
| 34 | def wrapper(self, other): | 36 | def wrapper(self, other): | ||
| 35 | res = method(self, other) | 37 | res = method(self, other) | ||
| 36 | setattr(self, f'__{idx}', True) | 38 | setattr(self, f'__{idx}', True) | ||
| 37 | if type(other) is Potion: | 39 | if type(other) is Potion: | ||
| 38 | setattr(other, f'__{idx}', True) | 40 | setattr(other, f'__{idx}', True) | ||
| 39 | return res | 41 | return res | ||
| 40 | 42 | ||||
| 41 | return wrapper | 43 | return wrapper | ||
| 42 | 44 | ||||
| 43 | return once | 45 | return once | ||
| 44 | 46 | ||||
| 45 | 47 | ||||
| 46 | @使ったら消えてしまう(DEPLETED_E) | 48 | @使ったら消えてしまう(DEPLETED_E) | ||
| 47 | class Effect: | 49 | class Effect: | ||
| 48 | @staticmethod | 50 | @staticmethod | ||
| 49 | def mole_mass(effect): | 51 | def mole_mass(effect): | ||
| 50 | return sum(ord(char) for char in effect) | 52 | return sum(ord(char) for char in effect) | ||
| 51 | 53 | ||||
| 52 | def __init__(self, effect): | 54 | def __init__(self, effect): | ||
| 53 | self.effect = effect | 55 | self.effect = effect | ||
| 54 | self.intensity = 1 | 56 | self.intensity = 1 | ||
| 55 | 57 | ||||
| 56 | @消えたら伝えろ(0) | 58 | @消えたら伝えろ(0) | ||
| 57 | def __call__(self, target): | 59 | def __call__(self, target): | ||
| 58 | while self.intensity > 0: | 60 | while self.intensity > 0: | ||
| 59 | self.effect(target) | 61 | self.effect(target) | ||
| 60 | self.intensity -= 1 | 62 | self.intensity -= 1 | ||
| n | 61 | n | |||
| 62 | def __eq__(self, other): | ||||
| 63 | return self.effect == other.effect and self.intensity == other.intensity | ||||
| 64 | 63 | ||||
| 65 | 64 | ||||
| 66 | @使ったら消えてしまう(DEPLETED_P, TRANSCENDED) | 65 | @使ったら消えてしまう(DEPLETED_P, TRANSCENDED) | ||
| 67 | class Potion: | 66 | class Potion: | ||
| 68 | def __init__(self, effects, duration): | 67 | def __init__(self, effects, duration): | ||
| 69 | self.effects = effects | 68 | self.effects = effects | ||
| 70 | self.duration = duration | 69 | self.duration = duration | ||
| 71 | for effect_name, effect in effects.items(): | 70 | for effect_name, effect in effects.items(): | ||
| 72 | setattr(self, effect_name, Effect(effect)) | 71 | setattr(self, effect_name, Effect(effect)) | ||
| 73 | 72 | ||||
| 74 | def __getitem__(self, effect): | 73 | def __getitem__(self, effect): | ||
| 75 | return getattr(self, effect) | 74 | return getattr(self, effect) | ||
| 76 | 75 | ||||
| 77 | def __setitem__(self, effect_name, effect): | 76 | def __setitem__(self, effect_name, effect): | ||
| 78 | self.effects[effect_name] = effect | 77 | self.effects[effect_name] = effect | ||
| 79 | setattr(self, effect_name, Effect(effect)) | 78 | setattr(self, effect_name, Effect(effect)) | ||
| 80 | 79 | ||||
| 81 | def __delitem__(self, key): | 80 | def __delitem__(self, key): | ||
| 82 | delattr(self, key) | 81 | delattr(self, key) | ||
| 83 | del self.effects[key] | 82 | del self.effects[key] | ||
| 84 | 83 | ||||
| 85 | @消えたら伝えろ(1) | 84 | @消えたら伝えろ(1) | ||
| 86 | def __add__(self, other): | 85 | def __add__(self, other): | ||
| 87 | potion = Potion({}, max(self.duration, other.duration)) | 86 | potion = Potion({}, max(self.duration, other.duration)) | ||
| 88 | potion._copy_unused_effects(self) | 87 | potion._copy_unused_effects(self) | ||
| 89 | potion._copy_unused_effects(other) | 88 | potion._copy_unused_effects(other) | ||
| 90 | for effect in set(self.effects) & set(other.effects): | 89 | for effect in set(self.effects) & set(other.effects): | ||
| 91 | with suppress(TypeError): | 90 | with suppress(TypeError): | ||
| 92 | potion[effect].intensity = self[effect].intensity + other[effect].intensity | 91 | potion[effect].intensity = self[effect].intensity + other[effect].intensity | ||
| 93 | return potion | 92 | return potion | ||
| 94 | 93 | ||||
| 95 | @消えたら伝えろ(1) | 94 | @消えたら伝えろ(1) | ||
| 96 | def __sub__(self, other): | 95 | def __sub__(self, other): | ||
| 97 | if set(other.effects) - set(self.effects): | 96 | if set(other.effects) - set(self.effects): | ||
| 98 | raise TypeError | 97 | raise TypeError | ||
| 99 | potion = Potion({}, self.duration) | 98 | potion = Potion({}, self.duration) | ||
| 100 | potion._copy_unused_effects(self) # do we need it? | 99 | potion._copy_unused_effects(self) # do we need it? | ||
| 101 | for effect in other.effects: | 100 | for effect in other.effects: | ||
| 102 | with suppress(TypeError): | 101 | with suppress(TypeError): | ||
| 103 | intensity = self[effect].intensity - other[effect].intensity | 102 | intensity = self[effect].intensity - other[effect].intensity | ||
| 104 | if intensity <= 0: | 103 | if intensity <= 0: | ||
| 105 | del potion[effect] | 104 | del potion[effect] | ||
| 106 | else: | 105 | else: | ||
| 107 | potion[effect].intensity = intensity | 106 | potion[effect].intensity = intensity | ||
| 108 | return potion | 107 | return potion | ||
| 109 | 108 | ||||
| 110 | @消えたら伝えろ(1) | 109 | @消えたら伝えろ(1) | ||
| 111 | def __mul__(self, other): | 110 | def __mul__(self, other): | ||
| 112 | potion = Potion({}, self.duration) | 111 | potion = Potion({}, self.duration) | ||
| 113 | potion._copy_unused_effects(self) | 112 | potion._copy_unused_effects(self) | ||
| 114 | for effect in self.effects: | 113 | for effect in self.effects: | ||
| 115 | with suppress(TypeError): | 114 | with suppress(TypeError): | ||
| 116 | potion[effect].intensity = round_half_down(self[effect].intensity * other) | 115 | potion[effect].intensity = round_half_down(self[effect].intensity * other) | ||
| 117 | return potion | 116 | return potion | ||
| 118 | 117 | ||||
| 119 | @消えたら伝えろ(1) | 118 | @消えたら伝えろ(1) | ||
| 120 | def __truediv__(self, other): | 119 | def __truediv__(self, other): | ||
| 121 | potions = [] | 120 | potions = [] | ||
| 122 | for _ in range(other): | 121 | for _ in range(other): | ||
| 123 | potion = Potion({}, round_half_down(self.duration / other)) | 122 | potion = Potion({}, round_half_down(self.duration / other)) | ||
| 124 | potion._copy_unused_effects(self) | 123 | potion._copy_unused_effects(self) | ||
| 125 | for effect in self.effects: | 124 | for effect in self.effects: | ||
| 126 | with suppress(TypeError): | 125 | with suppress(TypeError): | ||
| 127 | potion[effect].intensity = round_half_down(self[effect].intensity / other) | 126 | potion[effect].intensity = round_half_down(self[effect].intensity / other) | ||
| 128 | potions.append(potion) | 127 | potions.append(potion) | ||
| 129 | return potions | 128 | return potions | ||
| 130 | 129 | ||||
| 131 | def __eq__(self, other): | 130 | def __eq__(self, other): | ||
| n | 132 | if self.effects != other.effects: | n | 131 | if self.effects.keys() != other.effects.keys(): |
| 133 | return False | 132 | return False | ||
| 134 | for effect in self.effects: | 133 | for effect in self.effects: | ||
| 135 | intensity_1, intensity_2 = 0, 0 | 134 | intensity_1, intensity_2 = 0, 0 | ||
| 136 | with suppress(TypeError): | 135 | with suppress(TypeError): | ||
| 137 | intensity_1 = self[effect].intensity | 136 | intensity_1 = self[effect].intensity | ||
| 138 | with suppress(TypeError): | 137 | with suppress(TypeError): | ||
| 139 | intensity_2 = other[effect].intensity | 138 | intensity_2 = other[effect].intensity | ||
| 140 | if intensity_1 != intensity_2: | 139 | if intensity_1 != intensity_2: | ||
| 141 | return False | 140 | return False | ||
| 142 | return True | 141 | return True | ||
| 143 | 142 | ||||
| 144 | def __gt__(self, other): | 143 | def __gt__(self, other): | ||
| 145 | return self._total_intensities() > other._total_intensities() | 144 | return self._total_intensities() > other._total_intensities() | ||
| 146 | 145 | ||||
| 147 | @消えたら伝えろ(0) | 146 | @消えたら伝えろ(0) | ||
| 148 | def __call__(self, target): | 147 | def __call__(self, target): | ||
| 149 | for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass): | 148 | for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass): | ||
| 150 | with suppress(TypeError): | 149 | with suppress(TypeError): | ||
| 151 | self[effect](target) | 150 | self[effect](target) | ||
| 152 | 151 | ||||
| 153 | def __neg__(self): | 152 | def __neg__(self): | ||
| 154 | potion = Potion({}, self.duration - 1) | 153 | potion = Potion({}, self.duration - 1) | ||
| 155 | potion._copy_unused_effects(self) | 154 | potion._copy_unused_effects(self) | ||
| 156 | return potion | 155 | return potion | ||
| 157 | 156 | ||||
| n | 158 | def __pos__(self): | n | ||
| 159 | potion = Potion({}, self.duration) | ||||
| 160 | potion._copy_unused_effects(self) | ||||
| 161 | return potion | ||||
| 162 | |||||
| 163 | def _total_intensities(self): | 157 | def _total_intensities(self): | ||
| 164 | total = 0 | 158 | total = 0 | ||
| 165 | for effect in self.effects: | 159 | for effect in self.effects: | ||
| 166 | with suppress(TypeError): | 160 | with suppress(TypeError): | ||
| 167 | total += self[effect].intensity | 161 | total += self[effect].intensity | ||
| 168 | return total | 162 | return total | ||
| 169 | 163 | ||||
| 170 | def _copy_unused_effects(self, other): | 164 | def _copy_unused_effects(self, other): | ||
| 171 | for effect in other.effects: | 165 | for effect in other.effects: | ||
| 172 | with suppress(TypeError): | 166 | with suppress(TypeError): | ||
| 173 | self._add_effect(effect, other.effects[effect], other[effect].intensity) | 167 | self._add_effect(effect, other.effects[effect], other[effect].intensity) | ||
| 174 | 168 | ||||
| 175 | def _add_effect(self, effect_name, effect, intensity=1): | 169 | def _add_effect(self, effect_name, effect, intensity=1): | ||
| 176 | self[effect_name] = effect | 170 | self[effect_name] = effect | ||
| 177 | self[effect_name].intensity = intensity | 171 | self[effect_name].intensity = intensity | ||
| 178 | 172 | ||||
| 179 | 173 | ||||
| 180 | class Addict: | 174 | class Addict: | ||
| n | 181 | def __init__(self, addict, potion, original): | n | 175 | def __init__(self, addict, potion): |
| 182 | self.addict = addict | 176 | self.addict = addict | ||
| 183 | self.potion = potion | 177 | self.potion = potion | ||
| n | 184 | self.original = original | n | ||
| 185 | self.current = addict.__dict__ | 178 | self.current = addict.__dict__ | ||
| 186 | self.changed = {} | 179 | self.changed = {} | ||
| 187 | self.added = set() | 180 | self.added = set() | ||
| 188 | 181 | ||||
| n | n | 182 | def __repr__(self): | ||
| 183 | return f'{id(self.addict)} with state {self.current}' | ||||
| 184 | |||||
| 189 | 185 | ||||
| 190 | class ГоспожатаПоХимия: | 186 | class ГоспожатаПоХимия: | ||
| n | 191 | _addict_idx = 0 | n | ||
| 192 | |||||
| 193 | def __init__(self): | 187 | def __init__(self): | ||
| 194 | self.addicts = {} | 188 | self.addicts = {} | ||
| n | n | 189 | self.drugs = {} | ||
| 195 | 190 | ||||
| 196 | def apply(self, target, potion): | 191 | def apply(self, target, potion): | ||
| 197 | if potion.duration: | 192 | if potion.duration: | ||
| n | 198 | addict_idx = self._addict(target, potion) | n | 193 | addict_id = self._make_addict(target) |
| 194 | self._add_drug(addict_id, -potion) | ||||
| 199 | potion(target) | 195 | potion(target) | ||
| n | 200 | self._check_state(addict_idx) | n | ||
| 201 | 196 | ||||
| 202 | def tick(self): | 197 | def tick(self): | ||
| 203 | for addict in list(self.addicts): | 198 | for addict in list(self.addicts): | ||
| 204 | self._withdraw(addict) | 199 | self._withdraw(addict) | ||
| n | n | 200 | |||
| 201 | if not self.drugs[addict]: | ||||
| 205 | if self._run_out(addict): | 202 | self._remove(addict) | ||
| 206 | del self.addicts[addict] | ||||
| 207 | continue | 203 | continue | ||
| n | n | 204 | |||
| 208 | self._drug(addict) | 205 | self._give_drugs(addict) | ||
| 209 | 206 | ||||
| n | 210 | def _addict(self, target, drug): | n | 207 | def _make_addict(self, target): |
| 211 | idx = self._addict_idx | 208 | if id(target) not in self.addicts: | ||
| 212 | self.addicts[idx] = Addict(target, -drug, target.__dict__.copy()) | 209 | self.addicts[id(target)] = target, deepcopy(target.__dict__) | ||
| 213 | self._addict_idx += 1 | 210 | self.drugs[id(target)] = deque() | ||
| 214 | return idx | 211 | return id(target) | ||
| 215 | 212 | ||||
| n | 216 | def _check_state(self, addict_idx): | n | 213 | def _add_drug(self, addict_id, potion): |
| 217 | addict = self.addicts[addict_idx] | 214 | if potion.duration > 0: | ||
| 215 | self.drugs[addict_id].append(potion) | ||||
| 218 | 216 | ||||
| n | 219 | addict.added = {attr for attr in set(addict.current) - set(addict.original)} | n | ||
| 220 | for attr in set(addict.original) & set(addict.current): | ||||
| 221 | if addict.original[attr] != addict.current[attr]: | ||||
| 222 | addict.changed[attr] = addict.original[attr] | ||||
| 223 | addict.changed |= {attr: addict.original[attr] for attr in set(addict.original) - set(addict.current)} | ||||
| 224 | |||||
| 225 | def _withdraw(self, addict_idx): | 217 | def _withdraw(self, addict_id): | ||
| 218 | self.addicts[addict_id][0].__dict__ = deepcopy(self.addicts[addict_id][1]) | ||||
| 219 | |||||
| 220 | def _give_drugs(self, addict_id): | ||||
| 221 | drugs = self.drugs[addict_id] | ||||
| 226 | addict = self.addicts[addict_idx] | 222 | addict = self.addicts[addict_id][0] | ||
| 227 | target = addict.addict | 223 | drugs_to_take = len(drugs) | ||
| 228 | for attr in addict.added: | ||||
| 229 | del target.__dict__[attr] | ||||
| 230 | for attr in addict.changed: | ||||
| 231 | target.__dict__[attr] = addict.changed[attr] | ||||
| 232 | 224 | ||||
| n | 233 | def _run_out(self, addict_idx): | n | 225 | while drugs_to_take > 0: |
| 234 | return self.addicts[addict_idx].potion.duration == 0 | 226 | drug = drugs.popleft() | ||
| 227 | self._add_drug(addict_id, -drug) | ||||
| 228 | drug(addict) | ||||
| 229 | drugs_to_take -= 1 | ||||
| 235 | 230 | ||||
| t | 236 | def _drug(self, addict_idx): | t | 231 | def _remove(self, addict_id): |
| 237 | addict = self.addicts[addict_idx] | 232 | del self.addicts[addict_id] | ||
| 238 | drug = addict.potion | 233 | del self.drugs[addict_id] | ||
| 239 | addict.potion = -drug | ||||
| 240 | drug(addict.addict) |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | import math | f | 1 | import math |
| 2 | from contextlib import suppress | 2 | from contextlib import suppress | ||
| n | 3 | from collections import deque | n | ||
| 4 | 3 | ||||
| 5 | TRANSCENDED = 'Potion is now part of something bigger than itself.' | 4 | TRANSCENDED = 'Potion is now part of something bigger than itself.' | ||
| 6 | DEPLETED_P = 'Potion is depleted.' | 5 | DEPLETED_P = 'Potion is depleted.' | ||
| 7 | DEPLETED_E = 'Effect is depleted.' | 6 | DEPLETED_E = 'Effect is depleted.' | ||
| 8 | 7 | ||||
| 9 | 8 | ||||
| 10 | def round_half_down(num): | 9 | def round_half_down(num): | ||
| 11 | return math.ceil(num - 0.5) | 10 | return math.ceil(num - 0.5) | ||
| 12 | 11 | ||||
| 13 | 12 | ||||
| n | 14 | def към_оня_свят(*msgs): | n | 13 | def 使ったら消えてしまう(*msgs): |
| 15 | def decorator(cls): | 14 | def decorator(cls): | ||
| 16 | class UseOnce(cls): | 15 | class UseOnce(cls): | ||
| 17 | def __init__(self, *args, **kwargs): | 16 | def __init__(self, *args, **kwargs): | ||
| 18 | super().__init__(*args, **kwargs) | 17 | super().__init__(*args, **kwargs) | ||
| 19 | for idx in range(len(msgs)): | 18 | for idx in range(len(msgs)): | ||
| 20 | setattr(self, f'__{idx}', False) | 19 | setattr(self, f'__{idx}', False) | ||
| 21 | 20 | ||||
| 22 | def __getattribute__(self, item): | 21 | def __getattribute__(self, item): | ||
| 23 | for idx in range(len(msgs)): | 22 | for idx in range(len(msgs)): | ||
| 24 | if object.__getattribute__(self, f'__{idx}'): | 23 | if object.__getattribute__(self, f'__{idx}'): | ||
| 25 | raise TypeError(msgs[idx]) | 24 | raise TypeError(msgs[idx]) | ||
| 26 | return super().__getattribute__(item) | 25 | return super().__getattribute__(item) | ||
| 27 | 26 | ||||
| 28 | return UseOnce | 27 | return UseOnce | ||
| 29 | 28 | ||||
| 30 | return decorator | 29 | return decorator | ||
| 31 | 30 | ||||
| 32 | 31 | ||||
| n | 33 | def преход(idx=0): | n | 32 | def 消えたら伝えろ(idx): |
| 34 | def once(method): | 33 | def once(method): | ||
| 35 | def wrapper(self, other): | 34 | def wrapper(self, other): | ||
| 36 | res = method(self, other) | 35 | res = method(self, other) | ||
| 37 | setattr(self, f'__{idx}', True) | 36 | setattr(self, f'__{idx}', True) | ||
| 38 | if type(other) is Potion: | 37 | if type(other) is Potion: | ||
| 39 | setattr(other, f'__{idx}', True) | 38 | setattr(other, f'__{idx}', True) | ||
| 40 | return res | 39 | return res | ||
| 41 | 40 | ||||
| 42 | return wrapper | 41 | return wrapper | ||
| 43 | 42 | ||||
| 44 | return once | 43 | return once | ||
| 45 | 44 | ||||
| 46 | 45 | ||||
| n | 47 | @към_оня_свят(DEPLETED_E) | n | 46 | @使ったら消えてしまう(DEPLETED_E) |
| 48 | class Effect: | 47 | class Effect: | ||
| 49 | @staticmethod | 48 | @staticmethod | ||
| 50 | def mole_mass(effect): | 49 | def mole_mass(effect): | ||
| 51 | return sum(ord(char) for char in effect) | 50 | return sum(ord(char) for char in effect) | ||
| 52 | 51 | ||||
| 53 | def __init__(self, effect): | 52 | def __init__(self, effect): | ||
| 54 | self.effect = effect | 53 | self.effect = effect | ||
| 55 | self.intensity = 1 | 54 | self.intensity = 1 | ||
| 56 | 55 | ||||
| n | 57 | @преход() | n | 56 | @消えたら伝えろ(0) |
| 58 | def __call__(self, target): | 57 | def __call__(self, target): | ||
| 59 | while self.intensity > 0: | 58 | while self.intensity > 0: | ||
| 60 | self.effect(target) | 59 | self.effect(target) | ||
| 61 | self.intensity -= 1 | 60 | self.intensity -= 1 | ||
| 62 | 61 | ||||
| 63 | def __eq__(self, other): | 62 | def __eq__(self, other): | ||
| 64 | return self.effect == other.effect and self.intensity == other.intensity | 63 | return self.effect == other.effect and self.intensity == other.intensity | ||
| 65 | 64 | ||||
| 66 | 65 | ||||
| n | 67 | @към_оня_свят(DEPLETED_P, TRANSCENDED) | n | 66 | @使ったら消えてしまう(DEPLETED_P, TRANSCENDED) |
| 68 | class Potion: | 67 | class Potion: | ||
| 69 | def __init__(self, effects, duration): | 68 | def __init__(self, effects, duration): | ||
| 70 | self.effects = effects | 69 | self.effects = effects | ||
| 71 | self.duration = duration | 70 | self.duration = duration | ||
| 72 | for effect_name, effect in effects.items(): | 71 | for effect_name, effect in effects.items(): | ||
| 73 | setattr(self, effect_name, Effect(effect)) | 72 | setattr(self, effect_name, Effect(effect)) | ||
| 74 | 73 | ||||
| 75 | def __getitem__(self, effect): | 74 | def __getitem__(self, effect): | ||
| 76 | return getattr(self, effect) | 75 | return getattr(self, effect) | ||
| 77 | 76 | ||||
| 78 | def __setitem__(self, effect_name, effect): | 77 | def __setitem__(self, effect_name, effect): | ||
| 79 | self.effects[effect_name] = effect | 78 | self.effects[effect_name] = effect | ||
| 80 | setattr(self, effect_name, Effect(effect)) | 79 | setattr(self, effect_name, Effect(effect)) | ||
| 81 | 80 | ||||
| 82 | def __delitem__(self, key): | 81 | def __delitem__(self, key): | ||
| 83 | delattr(self, key) | 82 | delattr(self, key) | ||
| 84 | del self.effects[key] | 83 | del self.effects[key] | ||
| 85 | 84 | ||||
| n | 86 | @преход(1) | n | 85 | @消えたら伝えろ(1) |
| 87 | def __add__(self, other): | 86 | def __add__(self, other): | ||
| 88 | potion = Potion({}, max(self.duration, other.duration)) | 87 | potion = Potion({}, max(self.duration, other.duration)) | ||
| 89 | potion._copy_unused_effects(self) | 88 | potion._copy_unused_effects(self) | ||
| 90 | potion._copy_unused_effects(other) | 89 | potion._copy_unused_effects(other) | ||
| 91 | for effect in set(self.effects) & set(other.effects): | 90 | for effect in set(self.effects) & set(other.effects): | ||
| 92 | with suppress(TypeError): | 91 | with suppress(TypeError): | ||
| 93 | potion[effect].intensity = self[effect].intensity + other[effect].intensity | 92 | potion[effect].intensity = self[effect].intensity + other[effect].intensity | ||
| 94 | return potion | 93 | return potion | ||
| 95 | 94 | ||||
| n | 96 | @преход(1) | n | 95 | @消えたら伝えろ(1) |
| 97 | def __sub__(self, other): | 96 | def __sub__(self, other): | ||
| 98 | if set(other.effects) - set(self.effects): | 97 | if set(other.effects) - set(self.effects): | ||
| 99 | raise TypeError | 98 | raise TypeError | ||
| 100 | potion = Potion({}, self.duration) | 99 | potion = Potion({}, self.duration) | ||
| 101 | potion._copy_unused_effects(self) # do we need it? | 100 | potion._copy_unused_effects(self) # do we need it? | ||
| 102 | for effect in other.effects: | 101 | for effect in other.effects: | ||
| 103 | with suppress(TypeError): | 102 | with suppress(TypeError): | ||
| 104 | intensity = self[effect].intensity - other[effect].intensity | 103 | intensity = self[effect].intensity - other[effect].intensity | ||
| 105 | if intensity <= 0: | 104 | if intensity <= 0: | ||
| 106 | del potion[effect] | 105 | del potion[effect] | ||
| 107 | else: | 106 | else: | ||
| 108 | potion[effect].intensity = intensity | 107 | potion[effect].intensity = intensity | ||
| 109 | return potion | 108 | return potion | ||
| 110 | 109 | ||||
| n | 111 | @преход(1) | n | 110 | @消えたら伝えろ(1) |
| 112 | def __mul__(self, other): | 111 | def __mul__(self, other): | ||
| 113 | potion = Potion({}, self.duration) | 112 | potion = Potion({}, self.duration) | ||
| 114 | potion._copy_unused_effects(self) | 113 | potion._copy_unused_effects(self) | ||
| 115 | for effect in self.effects: | 114 | for effect in self.effects: | ||
| 116 | with suppress(TypeError): | 115 | with suppress(TypeError): | ||
| 117 | potion[effect].intensity = round_half_down(self[effect].intensity * other) | 116 | potion[effect].intensity = round_half_down(self[effect].intensity * other) | ||
| 118 | return potion | 117 | return potion | ||
| 119 | 118 | ||||
| n | 120 | @преход(1) | n | 119 | @消えたら伝えろ(1) |
| 121 | def __truediv__(self, other): | 120 | def __truediv__(self, other): | ||
| 122 | potions = [] | 121 | potions = [] | ||
| 123 | for _ in range(other): | 122 | for _ in range(other): | ||
| 124 | potion = Potion({}, round_half_down(self.duration / other)) | 123 | potion = Potion({}, round_half_down(self.duration / other)) | ||
| 125 | potion._copy_unused_effects(self) | 124 | potion._copy_unused_effects(self) | ||
| 126 | for effect in self.effects: | 125 | for effect in self.effects: | ||
| 127 | with suppress(TypeError): | 126 | with suppress(TypeError): | ||
| 128 | potion[effect].intensity = round_half_down(self[effect].intensity / other) | 127 | potion[effect].intensity = round_half_down(self[effect].intensity / other) | ||
| 129 | potions.append(potion) | 128 | potions.append(potion) | ||
| 130 | return potions | 129 | return potions | ||
| 131 | 130 | ||||
| 132 | def __eq__(self, other): | 131 | def __eq__(self, other): | ||
| n | 133 | for effect in self.effects | other.effects: | n | 132 | if self.effects != other.effects: |
| 134 | try: | ||||
| 135 | if self[effect] != other[effect]: | ||||
| 136 | return False | 133 | return False | ||
| 137 | except AttributeError: | 134 | for effect in self.effects: | ||
| 135 | intensity_1, intensity_2 = 0, 0 | ||||
| 136 | with suppress(TypeError): | ||||
| 137 | intensity_1 = self[effect].intensity | ||||
| 138 | with suppress(TypeError): | ||||
| 139 | intensity_2 = other[effect].intensity | ||||
| 140 | if intensity_1 != intensity_2: | ||||
| 138 | return False | 141 | return False | ||
| 139 | return True | 142 | return True | ||
| 140 | 143 | ||||
| 141 | def __gt__(self, other): | 144 | def __gt__(self, other): | ||
| 142 | return self._total_intensities() > other._total_intensities() | 145 | return self._total_intensities() > other._total_intensities() | ||
| 143 | 146 | ||||
| n | 144 | @преход() | n | 147 | @消えたら伝えろ(0) |
| 145 | def __call__(self, target): | 148 | def __call__(self, target): | ||
| 146 | for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass): | 149 | for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass): | ||
| 147 | with suppress(TypeError): | 150 | with suppress(TypeError): | ||
| 148 | self[effect](target) | 151 | self[effect](target) | ||
| 149 | 152 | ||||
| 150 | def __neg__(self): | 153 | def __neg__(self): | ||
| 151 | potion = Potion({}, self.duration - 1) | 154 | potion = Potion({}, self.duration - 1) | ||
| n | n | 155 | potion._copy_unused_effects(self) | ||
| 156 | return potion | ||||
| 157 | |||||
| 158 | def __pos__(self): | ||||
| 159 | potion = Potion({}, self.duration) | ||||
| 152 | potion._copy_unused_effects(self) | 160 | potion._copy_unused_effects(self) | ||
| 153 | return potion | 161 | return potion | ||
| 154 | 162 | ||||
| 155 | def _total_intensities(self): | 163 | def _total_intensities(self): | ||
| 156 | total = 0 | 164 | total = 0 | ||
| 157 | for effect in self.effects: | 165 | for effect in self.effects: | ||
| 158 | with suppress(TypeError): | 166 | with suppress(TypeError): | ||
| 159 | total += self[effect].intensity | 167 | total += self[effect].intensity | ||
| 160 | return total | 168 | return total | ||
| 161 | 169 | ||||
| 162 | def _copy_unused_effects(self, other): | 170 | def _copy_unused_effects(self, other): | ||
| 163 | for effect in other.effects: | 171 | for effect in other.effects: | ||
| 164 | with suppress(TypeError): | 172 | with suppress(TypeError): | ||
| 165 | self._add_effect(effect, other.effects[effect], other[effect].intensity) | 173 | self._add_effect(effect, other.effects[effect], other[effect].intensity) | ||
| 166 | 174 | ||||
| 167 | def _add_effect(self, effect_name, effect, intensity=1): | 175 | def _add_effect(self, effect_name, effect, intensity=1): | ||
| 168 | self[effect_name] = effect | 176 | self[effect_name] = effect | ||
| 169 | self[effect_name].intensity = intensity | 177 | self[effect_name].intensity = intensity | ||
| 170 | 178 | ||||
| 171 | 179 | ||||
| n | n | 180 | class Addict: | ||
| 181 | def __init__(self, addict, potion, original): | ||||
| 182 | self.addict = addict | ||||
| 183 | self.potion = potion | ||||
| 184 | self.original = original | ||||
| 185 | self.current = addict.__dict__ | ||||
| 186 | self.changed = {} | ||||
| 187 | self.added = set() | ||||
| 188 | |||||
| 189 | |||||
| 172 | class ГоспожатаПоХимия: | 190 | class ГоспожатаПоХимия: | ||
| n | n | 191 | _addict_idx = 0 | ||
| 192 | |||||
| 173 | def __init__(self): | 193 | def __init__(self): | ||
| 174 | self.addicts = {} | 194 | self.addicts = {} | ||
| n | 175 | self.drugs = {} | n | ||
| 176 | 195 | ||||
| 177 | def apply(self, target, potion): | 196 | def apply(self, target, potion): | ||
| n | n | 197 | if potion.duration: | ||
| 178 | addict_id = self._make_addict(target) | 198 | addict_idx = self._addict(target, potion) | ||
| 179 | self._add_drug(addict_id, -potion) | ||||
| 180 | potion(target) | 199 | potion(target) | ||
| 200 | self._check_state(addict_idx) | ||||
| 181 | 201 | ||||
| 182 | def tick(self): | 202 | def tick(self): | ||
| n | 183 | for addict in self.addicts: | n | 203 | for addict in list(self.addicts): |
| 184 | self._withdraw(addict) | 204 | self._withdraw(addict) | ||
| n | n | 205 | if self._run_out(addict): | ||
| 206 | del self.addicts[addict] | ||||
| 207 | continue | ||||
| 185 | self._give_drugs(addict) | 208 | self._drug(addict) | ||
| 186 | 209 | ||||
| n | 187 | def _make_addict(self, target): | n | 210 | def _addict(self, target, drug): |
| 188 | if id(target) not in self.addicts: | 211 | idx = self._addict_idx | ||
| 189 | self.addicts[id(target)] = target, target.__dict__.copy() | 212 | self.addicts[idx] = Addict(target, -drug, target.__dict__.copy()) | ||
| 190 | self.drugs[id(target)] = deque() | 213 | self._addict_idx += 1 | ||
| 191 | return id(target) | 214 | return idx | ||
| 192 | 215 | ||||
| n | 193 | def _add_drug(self, addict_id, potion): | n | 216 | def _check_state(self, addict_idx): |
| 194 | if potion.duration > 0: | 217 | addict = self.addicts[addict_idx] | ||
| 195 | self.drugs[addict_id].append(potion) | ||||
| 196 | 218 | ||||
| t | t | 219 | addict.added = {attr for attr in set(addict.current) - set(addict.original)} | ||
| 220 | for attr in set(addict.original) & set(addict.current): | ||||
| 221 | if addict.original[attr] != addict.current[attr]: | ||||
| 222 | addict.changed[attr] = addict.original[attr] | ||||
| 223 | addict.changed |= {attr: addict.original[attr] for attr in set(addict.original) - set(addict.current)} | ||||
| 224 | |||||
| 197 | def _withdraw(self, addict_id): | 225 | def _withdraw(self, addict_idx): | ||
| 198 | self.addicts[addict_id][0].__dict__ = self.addicts[addict_id][1].copy() | ||||
| 199 | |||||
| 200 | def _give_drugs(self, addict_id): | ||||
| 201 | drugs = self.drugs[addict_id] | ||||
| 202 | addict = self.addicts[addict_id][0] | 226 | addict = self.addicts[addict_idx] | ||
| 203 | drugs_to_take = len(drugs) | 227 | target = addict.addict | ||
| 204 | while drugs_to_take > 0: | 228 | for attr in addict.added: | ||
| 205 | drug = drugs.popleft() | 229 | del target.__dict__[attr] | ||
| 206 | self._add_drug(addict_id, -drug) | 230 | for attr in addict.changed: | ||
| 231 | target.__dict__[attr] = addict.changed[attr] | ||||
| 232 | |||||
| 233 | def _run_out(self, addict_idx): | ||||
| 234 | return self.addicts[addict_idx].potion.duration == 0 | ||||
| 235 | |||||
| 236 | def _drug(self, addict_idx): | ||||
| 237 | addict = self.addicts[addict_idx] | ||||
| 238 | drug = addict.potion | ||||
| 239 | addict.potion = -drug | ||||
| 207 | drug(addict) | 240 | drug(addict.addict) | ||
| 208 | drugs_to_take -= 1 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| n | n | 1 | import math | ||
| 2 | from contextlib import suppress | ||||
| 3 | from collections import deque | ||||
| 4 | |||||
| 5 | TRANSCENDED = 'Potion is now part of something bigger than itself.' | ||||
| 6 | DEPLETED_P = 'Potion is depleted.' | ||||
| 7 | DEPLETED_E = 'Effect is depleted.' | ||||
| 8 | |||||
| 9 | |||||
| 10 | def round_half_down(num): | ||||
| 11 | return math.ceil(num - 0.5) | ||||
| 12 | |||||
| 13 | |||||
| 14 | def към_оня_свят(*msgs): | ||||
| 15 | def decorator(cls): | ||||
| 16 | class UseOnce(cls): | ||||
| 17 | def __init__(self, *args, **kwargs): | ||||
| 18 | super().__init__(*args, **kwargs) | ||||
| 19 | for idx in range(len(msgs)): | ||||
| 20 | setattr(self, f'__{idx}', False) | ||||
| 21 | |||||
| 22 | def __getattribute__(self, item): | ||||
| 23 | for idx in range(len(msgs)): | ||||
| 24 | if object.__getattribute__(self, f'__{idx}'): | ||||
| 25 | raise TypeError(msgs[idx]) | ||||
| 26 | return super().__getattribute__(item) | ||||
| 27 | |||||
| 28 | return UseOnce | ||||
| 29 | |||||
| 30 | return decorator | ||||
| 31 | |||||
| 32 | |||||
| 33 | def преход(idx=0): | ||||
| 34 | def once(method): | ||||
| 35 | def wrapper(self, other): | ||||
| 36 | res = method(self, other) | ||||
| 37 | setattr(self, f'__{idx}', True) | ||||
| 38 | if type(other) is Potion: | ||||
| 39 | setattr(other, f'__{idx}', True) | ||||
| 40 | return res | ||||
| 41 | |||||
| 42 | return wrapper | ||||
| 43 | |||||
| 44 | return once | ||||
| 45 | |||||
| 46 | |||||
| 47 | @към_оня_свят(DEPLETED_E) | ||||
| 48 | class Effect: | ||||
| 49 | @staticmethod | ||||
| 50 | def mole_mass(effect): | ||||
| 51 | return sum(ord(char) for char in effect) | ||||
| 52 | |||||
| 53 | def __init__(self, effect): | ||||
| 54 | self.effect = effect | ||||
| 55 | self.intensity = 1 | ||||
| 56 | |||||
| 57 | @преход() | ||||
| 58 | def __call__(self, target): | ||||
| 59 | while self.intensity > 0: | ||||
| 60 | self.effect(target) | ||||
| 61 | self.intensity -= 1 | ||||
| 62 | |||||
| 63 | def __eq__(self, other): | ||||
| 64 | return self.effect == other.effect and self.intensity == other.intensity | ||||
| 65 | |||||
| 66 | |||||
| 67 | @към_оня_свят(DEPLETED_P, TRANSCENDED) | ||||
| 1 | class Potion: | 68 | class Potion: | ||
| t | 2 | pass | t | 69 | def __init__(self, effects, duration): |
| 70 | self.effects = effects | ||||
| 71 | self.duration = duration | ||||
| 72 | for effect_name, effect in effects.items(): | ||||
| 73 | setattr(self, effect_name, Effect(effect)) | ||||
| 74 | |||||
| 75 | def __getitem__(self, effect): | ||||
| 76 | return getattr(self, effect) | ||||
| 77 | |||||
| 78 | def __setitem__(self, effect_name, effect): | ||||
| 79 | self.effects[effect_name] = effect | ||||
| 80 | setattr(self, effect_name, Effect(effect)) | ||||
| 81 | |||||
| 82 | def __delitem__(self, key): | ||||
| 83 | delattr(self, key) | ||||
| 84 | del self.effects[key] | ||||
| 85 | |||||
| 86 | @преход(1) | ||||
| 87 | def __add__(self, other): | ||||
| 88 | potion = Potion({}, max(self.duration, other.duration)) | ||||
| 89 | potion._copy_unused_effects(self) | ||||
| 90 | potion._copy_unused_effects(other) | ||||
| 91 | for effect in set(self.effects) & set(other.effects): | ||||
| 92 | with suppress(TypeError): | ||||
| 93 | potion[effect].intensity = self[effect].intensity + other[effect].intensity | ||||
| 94 | return potion | ||||
| 95 | |||||
| 96 | @преход(1) | ||||
| 97 | def __sub__(self, other): | ||||
| 98 | if set(other.effects) - set(self.effects): | ||||
| 99 | raise TypeError | ||||
| 100 | potion = Potion({}, self.duration) | ||||
| 101 | potion._copy_unused_effects(self) # do we need it? | ||||
| 102 | for effect in other.effects: | ||||
| 103 | with suppress(TypeError): | ||||
| 104 | intensity = self[effect].intensity - other[effect].intensity | ||||
| 105 | if intensity <= 0: | ||||
| 106 | del potion[effect] | ||||
| 107 | else: | ||||
| 108 | potion[effect].intensity = intensity | ||||
| 109 | return potion | ||||
| 110 | |||||
| 111 | @преход(1) | ||||
| 112 | def __mul__(self, other): | ||||
| 113 | potion = Potion({}, self.duration) | ||||
| 114 | potion._copy_unused_effects(self) | ||||
| 115 | for effect in self.effects: | ||||
| 116 | with suppress(TypeError): | ||||
| 117 | potion[effect].intensity = round_half_down(self[effect].intensity * other) | ||||
| 118 | return potion | ||||
| 119 | |||||
| 120 | @преход(1) | ||||
| 121 | def __truediv__(self, other): | ||||
| 122 | potions = [] | ||||
| 123 | for _ in range(other): | ||||
| 124 | potion = Potion({}, round_half_down(self.duration / other)) | ||||
| 125 | potion._copy_unused_effects(self) | ||||
| 126 | for effect in self.effects: | ||||
| 127 | with suppress(TypeError): | ||||
| 128 | potion[effect].intensity = round_half_down(self[effect].intensity / other) | ||||
| 129 | potions.append(potion) | ||||
| 130 | return potions | ||||
| 131 | |||||
| 132 | def __eq__(self, other): | ||||
| 133 | for effect in self.effects | other.effects: | ||||
| 134 | try: | ||||
| 135 | if self[effect] != other[effect]: | ||||
| 136 | return False | ||||
| 137 | except AttributeError: | ||||
| 138 | return False | ||||
| 139 | return True | ||||
| 140 | |||||
| 141 | def __gt__(self, other): | ||||
| 142 | return self._total_intensities() > other._total_intensities() | ||||
| 143 | |||||
| 144 | @преход() | ||||
| 145 | def __call__(self, target): | ||||
| 146 | for effect in sorted(self.effects, reverse=True, key=Effect.mole_mass): | ||||
| 147 | with suppress(TypeError): | ||||
| 148 | self[effect](target) | ||||
| 149 | |||||
| 150 | def __neg__(self): | ||||
| 151 | potion = Potion({}, self.duration - 1) | ||||
| 152 | potion._copy_unused_effects(self) | ||||
| 153 | return potion | ||||
| 154 | |||||
| 155 | def _total_intensities(self): | ||||
| 156 | total = 0 | ||||
| 157 | for effect in self.effects: | ||||
| 158 | with suppress(TypeError): | ||||
| 159 | total += self[effect].intensity | ||||
| 160 | return total | ||||
| 161 | |||||
| 162 | def _copy_unused_effects(self, other): | ||||
| 163 | for effect in other.effects: | ||||
| 164 | with suppress(TypeError): | ||||
| 165 | self._add_effect(effect, other.effects[effect], other[effect].intensity) | ||||
| 166 | |||||
| 167 | def _add_effect(self, effect_name, effect, intensity=1): | ||||
| 168 | self[effect_name] = effect | ||||
| 169 | self[effect_name].intensity = intensity | ||||
| 170 | |||||
| 171 | |||||
| 172 | class ГоспожатаПоХимия: | ||||
| 173 | def __init__(self): | ||||
| 174 | self.addicts = {} | ||||
| 175 | self.drugs = {} | ||||
| 176 | |||||
| 177 | def apply(self, target, potion): | ||||
| 178 | addict_id = self._make_addict(target) | ||||
| 179 | self._add_drug(addict_id, -potion) | ||||
| 180 | potion(target) | ||||
| 181 | |||||
| 182 | def tick(self): | ||||
| 183 | for addict in self.addicts: | ||||
| 184 | self._withdraw(addict) | ||||
| 185 | self._give_drugs(addict) | ||||
| 186 | |||||
| 187 | def _make_addict(self, target): | ||||
| 188 | if id(target) not in self.addicts: | ||||
| 189 | self.addicts[id(target)] = target, target.__dict__.copy() | ||||
| 190 | self.drugs[id(target)] = deque() | ||||
| 191 | return id(target) | ||||
| 192 | |||||
| 193 | def _add_drug(self, addict_id, potion): | ||||
| 194 | if potion.duration > 0: | ||||
| 195 | self.drugs[addict_id].append(potion) | ||||
| 196 | |||||
| 197 | def _withdraw(self, addict_id): | ||||
| 198 | self.addicts[addict_id][0].__dict__ = self.addicts[addict_id][1].copy() | ||||
| 199 | |||||
| 200 | def _give_drugs(self, addict_id): | ||||
| 201 | drugs = self.drugs[addict_id] | ||||
| 202 | addict = self.addicts[addict_id][0] | ||||
| 203 | drugs_to_take = len(drugs) | ||||
| 204 | while drugs_to_take > 0: | ||||
| 205 | drug = drugs.popleft() | ||||
| 206 | self._add_drug(addict_id, -drug) | ||||
| 207 | drug(addict) | ||||
| 208 | drugs_to_take -= 1 |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||