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 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|