1import copy
2
3
4class EffectWrapper:
5 """Effect decorator to handle intensity and single-usage."""
6
7 def __init__(self, function, intensity):
8 """Initializator."""
9 self.function = function
10 self.intensity = intensity
11 self.depleted = False
12
13 def __call__(self, target):
14 """Wrapper of the decorated function."""
15 if self.depleted:
16 raise TypeError("Effect is depleted.")
17 self.depleted = True
18 for _ in range(self.intensity):
19 self.function(target)
20
21
22class Potion:
23 """Potion representation."""
24
25 def __init__(self, effects, duration, intensities=None):
26 """Initializator."""
27 self.effects = effects
28 self.duration = duration
29 self.deprecated = False
30 self.depleted = False
31 self.intensities = intensities or {}
32 for key, val in effects.items():
33 self.intensities.setdefault(key, 1)
34 setattr(self, key, EffectWrapper(val, self.intensities[key]))
35
36 def __getattribute__(self, name):
37 """Prevent usage if the potion is deprecated."""
38 try:
39 if object.__getattribute__(self, 'depleted'):
40 raise TypeError("Potion is depleted.")
41 if object.__getattribute__(self, 'deprecated'):
42 raise TypeError("Potion is now part of something bigger than itself.")
43 if type(object.__getattribute__(self, name)) is EffectWrapper:
44 object.__getattribute__(self, 'intensities')[name] = 0
45 except AttributeError:
46 pass # deepcopy starts using this code before depleted and deprecated are available
47 return object.__getattribute__(self, name)
48
49 @staticmethod
50 def round_down(val):
51 """Round x.5 down, other cases - as normal."""
52 if val % 1 <= 0.5:
53 return int(val)
54 return int(val) + 1
55
56 def __add__(self, other):
57 """Combinations."""
58 effects = {}
59 intensities = {}
60 duration = max(self.duration, other.duration)
61 for key, val in self.effects.items():
62 intensities[key] = self.intensities[key]
63 effects[key] = val
64 for key, val in other.effects.items():
65 intensities[key] = intensities.get(key, 0) + other.intensities[key]
66 effects[key] = val
67 self.deprecated = True
68 other.deprecated = True
69 return Potion(effects, duration, intensities)
70
71 def __mul__(self, other):
72 """Potentiation/Dilution."""
73 effects = {}
74 intensities = {}
75 duration = self.duration
76 for key, val in self.effects.items():
77 intensities[key] = self.round_down(self.intensities[key] * other)
78 effects[key] = val
79 self.deprecated = True
80 return Potion(effects, duration, intensities)
81
82 def __sub__(self, other):
83 """Purification."""
84 effects = {}
85 intensities = {}
86 duration = self.duration
87 if set(other.effects) - set(self.effects):
88 raise TypeError('Effect mismatch.')
89 for key, val in self.effects.items():
90 if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0:
91 intensities[key] = intensity
92 effects[key] = val
93 self.deprecated = True
94 other.deprecated = True
95 return Potion(effects, duration, intensities)
96
97 def __truediv__(self, other):
98 """Separation."""
99 effects = {}
100 intensities = {}
101 duration = self.duration
102 for key, val in self.effects.items():
103 intensities[key] = self.round_down(self.intensities[key] / other)
104 effects[key] = val
105 self.deprecated = True
106 return tuple([Potion(effects, duration, intensities) for _ in range(other)])
107
108 def __eq__(self, other):
109 """Equal."""
110 return self.intensities == other.intensities
111
112 def __lt__(self, other):
113 """Less than."""
114 return sum(self.intensities.values()) < sum(other.intensities.values())
115
116
117class ГоспожатаПоХимия:
118 """Химичка Димитричка."""
119
120 def __init__(self):
121 """Initializator."""
122 self._target_cache = {}
123 self._potions_stack = {}
124
125 def _cache_target(self, target):
126 """Cache all public properties of a target."""
127 if target in self._target_cache:
128 return False
129 self._target_cache[target] = {}
130 for key, val in vars(target).items():
131 if key.startswith('_'):
132 continue
133 self._target_cache[target][key] = copy.deepcopy(val)
134
135 def _apply_potion(self, target, potion):
136 """Apply a potion to a target."""
137 potion_copy = copy.deepcopy(potion)
138 sorted_effects = sorted(potion.effects.keys(),
139 key=lambda x: sum(map(ord, x)),
140 reverse=True)
141 for key in sorted_effects:
142 try:
143 getattr(potion, key)(target)
144 except TypeError:
145 pass # This effect is not available
146 potion.depleted = True
147 return potion_copy
148
149 def _apply_all_potions(self, target):
150 """Apply all potions to a target one by one."""
151 # Set initial state
152 for attr_key, attr_val in self._target_cache[target].items():
153 setattr(target, attr_key, copy.deepcopy(attr_val))
154 # Apply all active potions
155 # and replace the used potion with a potion ready to be used again
156 if target in self._potions_stack:
157 for i, potion in enumerate(self._potions_stack[target]):
158 potion_copy = self._apply_potion(target, potion)
159 self._potions_stack[target][i] = potion_copy
160
161 def apply(self, target, potion):
162 """Apply a potion to a target."""
163 # Ensure potion is not depleted by trying to get an attribute
164 # Depleted potion will raise a TypeError if it is depleted
165 potion.depleted
166 self._cache_target(target)
167 self._potions_stack.setdefault(target, []).append(potion)
168 self._apply_all_potions(target)
169
170 def tick(self):
171 """Tick a turn."""
172 for target, potion_stack in self._potions_stack.items():
173 new_potion_stack = []
174 for potion in potion_stack:
175 potion.duration -= 1
176 if potion.duration > 0:
177 new_potion_stack.append(potion)
178 self._potions_stack[target] = new_potion_stack
179 self._apply_all_potions(target)
180 if not len(new_potion_stack):
181 del self._target_cache[target]
182 self._potions_stack = {key: val for key, val in self._potions_stack.items() if val}
183
184
185
186class Target:
187 """Test target."""
188
189 def __init__(self, **kwargs):
190 """Initializator."""
191 self._cache = kwargs
192 for key, val in kwargs.items():
193 setattr(self, key, val)
....................
----------------------------------------------------------------------
Ran 20 tests in 0.003s
OK
f | 1 | import copy | f | 1 | import copy |
2 | 2 | ||||
3 | 3 | ||||
4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
6 | 6 | ||||
7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
8 | """Initializator.""" | 8 | """Initializator.""" | ||
9 | self.function = function | 9 | self.function = function | ||
10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
11 | self.depleted = False | 11 | self.depleted = False | ||
12 | 12 | ||||
13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
15 | if self.depleted: | 15 | if self.depleted: | ||
16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
17 | self.depleted = True | 17 | self.depleted = True | ||
18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
19 | self.function(target) | 19 | self.function(target) | ||
20 | 20 | ||||
21 | 21 | ||||
22 | class Potion: | 22 | class Potion: | ||
23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
24 | 24 | ||||
25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
26 | """Initializator.""" | 26 | """Initializator.""" | ||
27 | self.effects = effects | 27 | self.effects = effects | ||
28 | self.duration = duration | 28 | self.duration = duration | ||
29 | self.deprecated = False | 29 | self.deprecated = False | ||
30 | self.depleted = False | 30 | self.depleted = False | ||
31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
35 | 35 | ||||
36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
38 | try: | 38 | try: | ||
39 | if object.__getattribute__(self, 'depleted'): | 39 | if object.__getattribute__(self, 'depleted'): | ||
40 | raise TypeError("Potion is depleted.") | 40 | raise TypeError("Potion is depleted.") | ||
41 | if object.__getattribute__(self, 'deprecated'): | 41 | if object.__getattribute__(self, 'deprecated'): | ||
42 | raise TypeError("Potion is now part of something bigger than itself.") | 42 | raise TypeError("Potion is now part of something bigger than itself.") | ||
n | n | 43 | if type(object.__getattribute__(self, name)) is EffectWrapper: | ||
44 | object.__getattribute__(self, 'intensities')[name] = 0 | ||||
43 | except AttributeError: | 45 | except AttributeError: | ||
44 | pass # deepcopy starts using this code before depleted and deprecated are available | 46 | pass # deepcopy starts using this code before depleted and deprecated are available | ||
45 | return object.__getattribute__(self, name) | 47 | return object.__getattribute__(self, name) | ||
46 | 48 | ||||
47 | @staticmethod | 49 | @staticmethod | ||
48 | def round_down(val): | 50 | def round_down(val): | ||
49 | """Round x.5 down, other cases - as normal.""" | 51 | """Round x.5 down, other cases - as normal.""" | ||
50 | if val % 1 <= 0.5: | 52 | if val % 1 <= 0.5: | ||
51 | return int(val) | 53 | return int(val) | ||
52 | return int(val) + 1 | 54 | return int(val) + 1 | ||
53 | 55 | ||||
54 | def __add__(self, other): | 56 | def __add__(self, other): | ||
55 | """Combinations.""" | 57 | """Combinations.""" | ||
56 | effects = {} | 58 | effects = {} | ||
57 | intensities = {} | 59 | intensities = {} | ||
58 | duration = max(self.duration, other.duration) | 60 | duration = max(self.duration, other.duration) | ||
59 | for key, val in self.effects.items(): | 61 | for key, val in self.effects.items(): | ||
60 | intensities[key] = self.intensities[key] | 62 | intensities[key] = self.intensities[key] | ||
61 | effects[key] = val | 63 | effects[key] = val | ||
62 | for key, val in other.effects.items(): | 64 | for key, val in other.effects.items(): | ||
63 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 65 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
64 | effects[key] = val | 66 | effects[key] = val | ||
65 | self.deprecated = True | 67 | self.deprecated = True | ||
66 | other.deprecated = True | 68 | other.deprecated = True | ||
67 | return Potion(effects, duration, intensities) | 69 | return Potion(effects, duration, intensities) | ||
68 | 70 | ||||
69 | def __mul__(self, other): | 71 | def __mul__(self, other): | ||
70 | """Potentiation/Dilution.""" | 72 | """Potentiation/Dilution.""" | ||
71 | effects = {} | 73 | effects = {} | ||
72 | intensities = {} | 74 | intensities = {} | ||
73 | duration = self.duration | 75 | duration = self.duration | ||
74 | for key, val in self.effects.items(): | 76 | for key, val in self.effects.items(): | ||
75 | intensities[key] = self.round_down(self.intensities[key] * other) | 77 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
76 | effects[key] = val | 78 | effects[key] = val | ||
77 | self.deprecated = True | 79 | self.deprecated = True | ||
78 | return Potion(effects, duration, intensities) | 80 | return Potion(effects, duration, intensities) | ||
79 | 81 | ||||
80 | def __sub__(self, other): | 82 | def __sub__(self, other): | ||
81 | """Purification.""" | 83 | """Purification.""" | ||
82 | effects = {} | 84 | effects = {} | ||
83 | intensities = {} | 85 | intensities = {} | ||
84 | duration = self.duration | 86 | duration = self.duration | ||
85 | if set(other.effects) - set(self.effects): | 87 | if set(other.effects) - set(self.effects): | ||
86 | raise TypeError('Effect mismatch.') | 88 | raise TypeError('Effect mismatch.') | ||
87 | for key, val in self.effects.items(): | 89 | for key, val in self.effects.items(): | ||
88 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | 90 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | ||
89 | intensities[key] = intensity | 91 | intensities[key] = intensity | ||
90 | effects[key] = val | 92 | effects[key] = val | ||
91 | self.deprecated = True | 93 | self.deprecated = True | ||
92 | other.deprecated = True | 94 | other.deprecated = True | ||
93 | return Potion(effects, duration, intensities) | 95 | return Potion(effects, duration, intensities) | ||
94 | 96 | ||||
95 | def __truediv__(self, other): | 97 | def __truediv__(self, other): | ||
96 | """Separation.""" | 98 | """Separation.""" | ||
97 | effects = {} | 99 | effects = {} | ||
98 | intensities = {} | 100 | intensities = {} | ||
99 | duration = self.duration | 101 | duration = self.duration | ||
100 | for key, val in self.effects.items(): | 102 | for key, val in self.effects.items(): | ||
101 | intensities[key] = self.round_down(self.intensities[key] / other) | 103 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
102 | effects[key] = val | 104 | effects[key] = val | ||
103 | self.deprecated = True | 105 | self.deprecated = True | ||
104 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | 106 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | ||
105 | 107 | ||||
106 | def __eq__(self, other): | 108 | def __eq__(self, other): | ||
107 | """Equal.""" | 109 | """Equal.""" | ||
108 | return self.intensities == other.intensities | 110 | return self.intensities == other.intensities | ||
109 | 111 | ||||
110 | def __lt__(self, other): | 112 | def __lt__(self, other): | ||
111 | """Less than.""" | 113 | """Less than.""" | ||
112 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 114 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
113 | 115 | ||||
114 | 116 | ||||
115 | class ГоспожатаПоХимия: | 117 | class ГоспожатаПоХимия: | ||
116 | """Химичка Димитричка.""" | 118 | """Химичка Димитричка.""" | ||
117 | 119 | ||||
118 | def __init__(self): | 120 | def __init__(self): | ||
119 | """Initializator.""" | 121 | """Initializator.""" | ||
120 | self._target_cache = {} | 122 | self._target_cache = {} | ||
121 | self._potions_stack = {} | 123 | self._potions_stack = {} | ||
122 | 124 | ||||
123 | def _cache_target(self, target): | 125 | def _cache_target(self, target): | ||
124 | """Cache all public properties of a target.""" | 126 | """Cache all public properties of a target.""" | ||
125 | if target in self._target_cache: | 127 | if target in self._target_cache: | ||
126 | return False | 128 | return False | ||
127 | self._target_cache[target] = {} | 129 | self._target_cache[target] = {} | ||
128 | for key, val in vars(target).items(): | 130 | for key, val in vars(target).items(): | ||
129 | if key.startswith('_'): | 131 | if key.startswith('_'): | ||
130 | continue | 132 | continue | ||
131 | self._target_cache[target][key] = copy.deepcopy(val) | 133 | self._target_cache[target][key] = copy.deepcopy(val) | ||
132 | 134 | ||||
133 | def _apply_potion(self, target, potion): | 135 | def _apply_potion(self, target, potion): | ||
134 | """Apply a potion to a target.""" | 136 | """Apply a potion to a target.""" | ||
135 | potion_copy = copy.deepcopy(potion) | 137 | potion_copy = copy.deepcopy(potion) | ||
136 | sorted_effects = sorted(potion.effects.keys(), | 138 | sorted_effects = sorted(potion.effects.keys(), | ||
137 | key=lambda x: sum(map(ord, x)), | 139 | key=lambda x: sum(map(ord, x)), | ||
138 | reverse=True) | 140 | reverse=True) | ||
139 | for key in sorted_effects: | 141 | for key in sorted_effects: | ||
140 | try: | 142 | try: | ||
141 | getattr(potion, key)(target) | 143 | getattr(potion, key)(target) | ||
142 | except TypeError: | 144 | except TypeError: | ||
143 | pass # This effect is not available | 145 | pass # This effect is not available | ||
144 | potion.depleted = True | 146 | potion.depleted = True | ||
145 | return potion_copy | 147 | return potion_copy | ||
146 | 148 | ||||
147 | def _apply_all_potions(self, target): | 149 | def _apply_all_potions(self, target): | ||
148 | """Apply all potions to a target one by one.""" | 150 | """Apply all potions to a target one by one.""" | ||
149 | # Set initial state | 151 | # Set initial state | ||
150 | for attr_key, attr_val in self._target_cache[target].items(): | 152 | for attr_key, attr_val in self._target_cache[target].items(): | ||
151 | setattr(target, attr_key, copy.deepcopy(attr_val)) | 153 | setattr(target, attr_key, copy.deepcopy(attr_val)) | ||
152 | # Apply all active potions | 154 | # Apply all active potions | ||
153 | # and replace the used potion with a potion ready to be used again | 155 | # and replace the used potion with a potion ready to be used again | ||
154 | if target in self._potions_stack: | 156 | if target in self._potions_stack: | ||
155 | for i, potion in enumerate(self._potions_stack[target]): | 157 | for i, potion in enumerate(self._potions_stack[target]): | ||
156 | potion_copy = self._apply_potion(target, potion) | 158 | potion_copy = self._apply_potion(target, potion) | ||
157 | self._potions_stack[target][i] = potion_copy | 159 | self._potions_stack[target][i] = potion_copy | ||
158 | 160 | ||||
159 | def apply(self, target, potion): | 161 | def apply(self, target, potion): | ||
160 | """Apply a potion to a target.""" | 162 | """Apply a potion to a target.""" | ||
161 | # Ensure potion is not depleted by trying to get an attribute | 163 | # Ensure potion is not depleted by trying to get an attribute | ||
162 | # Depleted potion will raise a TypeError if it is depleted | 164 | # Depleted potion will raise a TypeError if it is depleted | ||
163 | potion.depleted | 165 | potion.depleted | ||
164 | self._cache_target(target) | 166 | self._cache_target(target) | ||
165 | self._potions_stack.setdefault(target, []).append(potion) | 167 | self._potions_stack.setdefault(target, []).append(potion) | ||
166 | self._apply_all_potions(target) | 168 | self._apply_all_potions(target) | ||
167 | 169 | ||||
168 | def tick(self): | 170 | def tick(self): | ||
169 | """Tick a turn.""" | 171 | """Tick a turn.""" | ||
170 | for target, potion_stack in self._potions_stack.items(): | 172 | for target, potion_stack in self._potions_stack.items(): | ||
171 | new_potion_stack = [] | 173 | new_potion_stack = [] | ||
172 | for potion in potion_stack: | 174 | for potion in potion_stack: | ||
173 | potion.duration -= 1 | 175 | potion.duration -= 1 | ||
174 | if potion.duration > 0: | 176 | if potion.duration > 0: | ||
175 | new_potion_stack.append(potion) | 177 | new_potion_stack.append(potion) | ||
176 | self._potions_stack[target] = new_potion_stack | 178 | self._potions_stack[target] = new_potion_stack | ||
177 | self._apply_all_potions(target) | 179 | self._apply_all_potions(target) | ||
178 | if not len(new_potion_stack): | 180 | if not len(new_potion_stack): | ||
179 | del self._target_cache[target] | 181 | del self._target_cache[target] | ||
180 | self._potions_stack = {key: val for key, val in self._potions_stack.items() if val} | 182 | self._potions_stack = {key: val for key, val in self._potions_stack.items() if val} | ||
181 | 183 | ||||
t | t | 184 | |||
185 | |||||
186 | class Target: | ||||
187 | """Test target.""" | ||||
188 | |||||
189 | def __init__(self, **kwargs): | ||||
190 | """Initializator.""" | ||||
191 | self._cache = kwargs | ||||
192 | for key, val in kwargs.items(): | ||||
193 | setattr(self, key, val) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | import copy | f | 1 | import copy |
2 | 2 | ||||
3 | 3 | ||||
4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
6 | 6 | ||||
7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
8 | """Initializator.""" | 8 | """Initializator.""" | ||
9 | self.function = function | 9 | self.function = function | ||
10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
11 | self.depleted = False | 11 | self.depleted = False | ||
12 | 12 | ||||
13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
15 | if self.depleted: | 15 | if self.depleted: | ||
16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
17 | self.depleted = True | 17 | self.depleted = True | ||
18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
19 | self.function(target) | 19 | self.function(target) | ||
20 | 20 | ||||
21 | 21 | ||||
22 | class Potion: | 22 | class Potion: | ||
23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
24 | 24 | ||||
25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
26 | """Initializator.""" | 26 | """Initializator.""" | ||
27 | self.effects = effects | 27 | self.effects = effects | ||
28 | self.duration = duration | 28 | self.duration = duration | ||
29 | self.deprecated = False | 29 | self.deprecated = False | ||
30 | self.depleted = False | 30 | self.depleted = False | ||
31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
35 | 35 | ||||
36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
n | n | 38 | try: | ||
38 | if object.__getattribute__(self, 'depleted'): | 39 | if object.__getattribute__(self, 'depleted'): | ||
39 | raise TypeError("Potion is depleted.") | 40 | raise TypeError("Potion is depleted.") | ||
40 | if object.__getattribute__(self, 'deprecated'): | 41 | if object.__getattribute__(self, 'deprecated'): | ||
41 | raise TypeError("Potion is now part of something bigger than itself.") | 42 | raise TypeError("Potion is now part of something bigger than itself.") | ||
43 | except AttributeError: | ||||
44 | pass # deepcopy starts using this code before depleted and deprecated are available | ||||
42 | return object.__getattribute__(self, name) | 45 | return object.__getattribute__(self, name) | ||
43 | 46 | ||||
44 | @staticmethod | 47 | @staticmethod | ||
45 | def round_down(val): | 48 | def round_down(val): | ||
46 | """Round x.5 down, other cases - as normal.""" | 49 | """Round x.5 down, other cases - as normal.""" | ||
47 | if val % 1 <= 0.5: | 50 | if val % 1 <= 0.5: | ||
48 | return int(val) | 51 | return int(val) | ||
49 | return int(val) + 1 | 52 | return int(val) + 1 | ||
50 | 53 | ||||
51 | def __add__(self, other): | 54 | def __add__(self, other): | ||
52 | """Combinations.""" | 55 | """Combinations.""" | ||
53 | effects = {} | 56 | effects = {} | ||
54 | intensities = {} | 57 | intensities = {} | ||
55 | duration = max(self.duration, other.duration) | 58 | duration = max(self.duration, other.duration) | ||
56 | for key, val in self.effects.items(): | 59 | for key, val in self.effects.items(): | ||
57 | intensities[key] = self.intensities[key] | 60 | intensities[key] = self.intensities[key] | ||
58 | effects[key] = val | 61 | effects[key] = val | ||
59 | for key, val in other.effects.items(): | 62 | for key, val in other.effects.items(): | ||
60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 63 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
61 | effects[key] = val | 64 | effects[key] = val | ||
62 | self.deprecated = True | 65 | self.deprecated = True | ||
63 | other.deprecated = True | 66 | other.deprecated = True | ||
64 | return Potion(effects, duration, intensities) | 67 | return Potion(effects, duration, intensities) | ||
65 | 68 | ||||
66 | def __mul__(self, other): | 69 | def __mul__(self, other): | ||
67 | """Potentiation/Dilution.""" | 70 | """Potentiation/Dilution.""" | ||
68 | effects = {} | 71 | effects = {} | ||
69 | intensities = {} | 72 | intensities = {} | ||
70 | duration = self.duration | 73 | duration = self.duration | ||
71 | for key, val in self.effects.items(): | 74 | for key, val in self.effects.items(): | ||
72 | intensities[key] = self.round_down(self.intensities[key] * other) | 75 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
73 | effects[key] = val | 76 | effects[key] = val | ||
74 | self.deprecated = True | 77 | self.deprecated = True | ||
75 | return Potion(effects, duration, intensities) | 78 | return Potion(effects, duration, intensities) | ||
76 | 79 | ||||
77 | def __sub__(self, other): | 80 | def __sub__(self, other): | ||
78 | """Purification.""" | 81 | """Purification.""" | ||
79 | effects = {} | 82 | effects = {} | ||
80 | intensities = {} | 83 | intensities = {} | ||
81 | duration = self.duration | 84 | duration = self.duration | ||
82 | if set(other.effects) - set(self.effects): | 85 | if set(other.effects) - set(self.effects): | ||
83 | raise TypeError('Effect mismatch.') | 86 | raise TypeError('Effect mismatch.') | ||
84 | for key, val in self.effects.items(): | 87 | for key, val in self.effects.items(): | ||
85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | 88 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: | ||
86 | intensities[key] = intensity | 89 | intensities[key] = intensity | ||
87 | effects[key] = val | 90 | effects[key] = val | ||
88 | self.deprecated = True | 91 | self.deprecated = True | ||
89 | other.deprecated = True | 92 | other.deprecated = True | ||
90 | return Potion(effects, duration, intensities) | 93 | return Potion(effects, duration, intensities) | ||
91 | 94 | ||||
92 | def __truediv__(self, other): | 95 | def __truediv__(self, other): | ||
93 | """Separation.""" | 96 | """Separation.""" | ||
94 | effects = {} | 97 | effects = {} | ||
95 | intensities = {} | 98 | intensities = {} | ||
96 | duration = self.duration | 99 | duration = self.duration | ||
97 | for key, val in self.effects.items(): | 100 | for key, val in self.effects.items(): | ||
98 | intensities[key] = self.round_down(self.intensities[key] / other) | 101 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
99 | effects[key] = val | 102 | effects[key] = val | ||
100 | self.deprecated = True | 103 | self.deprecated = True | ||
101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | 104 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | ||
102 | 105 | ||||
103 | def __eq__(self, other): | 106 | def __eq__(self, other): | ||
104 | """Equal.""" | 107 | """Equal.""" | ||
105 | return self.intensities == other.intensities | 108 | return self.intensities == other.intensities | ||
106 | 109 | ||||
107 | def __lt__(self, other): | 110 | def __lt__(self, other): | ||
108 | """Less than.""" | 111 | """Less than.""" | ||
109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 112 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
110 | 113 | ||||
111 | 114 | ||||
112 | class ГоспожатаПоХимия: | 115 | class ГоспожатаПоХимия: | ||
113 | """Химичка Димитричка.""" | 116 | """Химичка Димитричка.""" | ||
114 | 117 | ||||
115 | def __init__(self): | 118 | def __init__(self): | ||
116 | """Initializator.""" | 119 | """Initializator.""" | ||
n | 117 | self._cache = {} | n | 120 | self._target_cache = {} |
121 | self._potions_stack = {} | ||||
118 | 122 | ||||
n | 119 | def _cache_target(self, target, duration): | n | 123 | def _cache_target(self, target): |
120 | """Cache all public properties of a target for restoring after a given duration.""" | 124 | """Cache all public properties of a target.""" | ||
121 | targets_cache = self._cache.setdefault(duration, {}) | 125 | if target in self._target_cache: | ||
122 | target_cache = targets_cache.setdefault(target, {}) | 126 | return False | ||
127 | self._target_cache[target] = {} | ||||
123 | for key, val in vars(target).items(): | 128 | for key, val in vars(target).items(): | ||
124 | if key.startswith('_'): | 129 | if key.startswith('_'): | ||
125 | continue | 130 | continue | ||
n | 126 | target_cache[key] = copy.copy(val) | n | 131 | self._target_cache[target][key] = copy.deepcopy(val) |
127 | 132 | ||||
n | 128 | def apply(self, target, potion): | n | 133 | def _apply_potion(self, target, potion): |
129 | """Apply a potion to a target.""" | 134 | """Apply a potion to a target.""" | ||
n | n | 135 | potion_copy = copy.deepcopy(potion) | ||
130 | sorted_effects = sorted(potion.effects.keys(), | 136 | sorted_effects = sorted(potion.effects.keys(), | ||
131 | key=lambda x: sum(map(ord, x)), | 137 | key=lambda x: sum(map(ord, x)), | ||
132 | reverse=True) | 138 | reverse=True) | ||
n | 133 | self._cache_target(target, potion.duration) | n | ||
134 | for key in sorted_effects: | 139 | for key in sorted_effects: | ||
135 | try: | 140 | try: | ||
136 | getattr(potion, key)(target) | 141 | getattr(potion, key)(target) | ||
137 | except TypeError: | 142 | except TypeError: | ||
138 | pass # This effect is not available | 143 | pass # This effect is not available | ||
139 | potion.depleted = True | 144 | potion.depleted = True | ||
n | n | 145 | return potion_copy | ||
146 | |||||
147 | def _apply_all_potions(self, target): | ||||
148 | """Apply all potions to a target one by one.""" | ||||
149 | # Set initial state | ||||
150 | for attr_key, attr_val in self._target_cache[target].items(): | ||||
151 | setattr(target, attr_key, copy.deepcopy(attr_val)) | ||||
152 | # Apply all active potions | ||||
153 | # and replace the used potion with a potion ready to be used again | ||||
154 | if target in self._potions_stack: | ||||
155 | for i, potion in enumerate(self._potions_stack[target]): | ||||
156 | potion_copy = self._apply_potion(target, potion) | ||||
157 | self._potions_stack[target][i] = potion_copy | ||||
158 | |||||
159 | def apply(self, target, potion): | ||||
160 | """Apply a potion to a target.""" | ||||
161 | # Ensure potion is not depleted by trying to get an attribute | ||||
162 | # Depleted potion will raise a TypeError if it is depleted | ||||
163 | potion.depleted | ||||
164 | self._cache_target(target) | ||||
165 | self._potions_stack.setdefault(target, []).append(potion) | ||||
166 | self._apply_all_potions(target) | ||||
140 | 167 | ||||
141 | def tick(self): | 168 | def tick(self): | ||
142 | """Tick a turn.""" | 169 | """Tick a turn.""" | ||
t | 143 | new_cache = {} | t | 170 | for target, potion_stack in self._potions_stack.items(): |
144 | for dur_key, dur_val in self._cache.items(): | 171 | new_potion_stack = [] | ||
145 | if not (ticked_dur_key := dur_key - 1): | 172 | for potion in potion_stack: | ||
146 | for target_key, target_val in dur_val.items(): | 173 | potion.duration -= 1 | ||
147 | for attr_key, attr_val in target_val.items(): | 174 | if potion.duration > 0: | ||
148 | setattr(target_key, attr_key, attr_val) | 175 | new_potion_stack.append(potion) | ||
149 | else: | 176 | self._potions_stack[target] = new_potion_stack | ||
150 | new_cache[ticked_dur_key] = dur_val | 177 | self._apply_all_potions(target) | ||
151 | self._cache = new_cache | 178 | if not len(new_potion_stack): | ||
179 | del self._target_cache[target] | ||||
180 | self._potions_stack = {key: val for key, val in self._potions_stack.items() if val} | ||||
181 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | import copy | f | 1 | import copy |
2 | 2 | ||||
3 | 3 | ||||
4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
6 | 6 | ||||
7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
8 | """Initializator.""" | 8 | """Initializator.""" | ||
9 | self.function = function | 9 | self.function = function | ||
10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
11 | self.depleted = False | 11 | self.depleted = False | ||
12 | 12 | ||||
13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
15 | if self.depleted: | 15 | if self.depleted: | ||
16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
17 | self.depleted = True | 17 | self.depleted = True | ||
18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
19 | self.function(target) | 19 | self.function(target) | ||
20 | 20 | ||||
21 | 21 | ||||
22 | class Potion: | 22 | class Potion: | ||
23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
24 | 24 | ||||
25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
26 | """Initializator.""" | 26 | """Initializator.""" | ||
27 | self.effects = effects | 27 | self.effects = effects | ||
28 | self.duration = duration | 28 | self.duration = duration | ||
29 | self.deprecated = False | 29 | self.deprecated = False | ||
30 | self.depleted = False | 30 | self.depleted = False | ||
31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
35 | 35 | ||||
36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
38 | if object.__getattribute__(self, 'depleted'): | 38 | if object.__getattribute__(self, 'depleted'): | ||
39 | raise TypeError("Potion is depleted.") | 39 | raise TypeError("Potion is depleted.") | ||
40 | if object.__getattribute__(self, 'deprecated'): | 40 | if object.__getattribute__(self, 'deprecated'): | ||
41 | raise TypeError("Potion is now part of something bigger than itself.") | 41 | raise TypeError("Potion is now part of something bigger than itself.") | ||
42 | return object.__getattribute__(self, name) | 42 | return object.__getattribute__(self, name) | ||
43 | 43 | ||||
44 | @staticmethod | 44 | @staticmethod | ||
45 | def round_down(val): | 45 | def round_down(val): | ||
46 | """Round x.5 down, other cases - as normal.""" | 46 | """Round x.5 down, other cases - as normal.""" | ||
47 | if val % 1 <= 0.5: | 47 | if val % 1 <= 0.5: | ||
48 | return int(val) | 48 | return int(val) | ||
49 | return int(val) + 1 | 49 | return int(val) + 1 | ||
50 | 50 | ||||
51 | def __add__(self, other): | 51 | def __add__(self, other): | ||
52 | """Combinations.""" | 52 | """Combinations.""" | ||
53 | effects = {} | 53 | effects = {} | ||
54 | intensities = {} | 54 | intensities = {} | ||
55 | duration = max(self.duration, other.duration) | 55 | duration = max(self.duration, other.duration) | ||
56 | for key, val in self.effects.items(): | 56 | for key, val in self.effects.items(): | ||
57 | intensities[key] = self.intensities[key] | 57 | intensities[key] = self.intensities[key] | ||
58 | effects[key] = val | 58 | effects[key] = val | ||
59 | for key, val in other.effects.items(): | 59 | for key, val in other.effects.items(): | ||
60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
61 | effects[key] = val | 61 | effects[key] = val | ||
62 | self.deprecated = True | 62 | self.deprecated = True | ||
63 | other.deprecated = True | 63 | other.deprecated = True | ||
64 | return Potion(effects, duration, intensities) | 64 | return Potion(effects, duration, intensities) | ||
65 | 65 | ||||
66 | def __mul__(self, other): | 66 | def __mul__(self, other): | ||
67 | """Potentiation/Dilution.""" | 67 | """Potentiation/Dilution.""" | ||
68 | effects = {} | 68 | effects = {} | ||
69 | intensities = {} | 69 | intensities = {} | ||
70 | duration = self.duration | 70 | duration = self.duration | ||
71 | for key, val in self.effects.items(): | 71 | for key, val in self.effects.items(): | ||
72 | intensities[key] = self.round_down(self.intensities[key] * other) | 72 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
73 | effects[key] = val | 73 | effects[key] = val | ||
74 | self.deprecated = True | 74 | self.deprecated = True | ||
75 | return Potion(effects, duration, intensities) | 75 | return Potion(effects, duration, intensities) | ||
76 | 76 | ||||
77 | def __sub__(self, other): | 77 | def __sub__(self, other): | ||
78 | """Purification.""" | 78 | """Purification.""" | ||
79 | effects = {} | 79 | effects = {} | ||
80 | intensities = {} | 80 | intensities = {} | ||
81 | duration = self.duration | 81 | duration = self.duration | ||
82 | if set(other.effects) - set(self.effects): | 82 | if set(other.effects) - set(self.effects): | ||
83 | raise TypeError('Effect mismatch.') | 83 | raise TypeError('Effect mismatch.') | ||
84 | for key, val in self.effects.items(): | 84 | for key, val in self.effects.items(): | ||
t | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | t | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0: |
86 | intensities[key] = intensity | 86 | intensities[key] = intensity | ||
87 | effects[key] = val | 87 | effects[key] = val | ||
88 | self.deprecated = True | 88 | self.deprecated = True | ||
89 | other.deprecated = True | 89 | other.deprecated = True | ||
90 | return Potion(effects, duration, intensities) | 90 | return Potion(effects, duration, intensities) | ||
91 | 91 | ||||
92 | def __truediv__(self, other): | 92 | def __truediv__(self, other): | ||
93 | """Separation.""" | 93 | """Separation.""" | ||
94 | effects = {} | 94 | effects = {} | ||
95 | intensities = {} | 95 | intensities = {} | ||
96 | duration = self.duration | 96 | duration = self.duration | ||
97 | for key, val in self.effects.items(): | 97 | for key, val in self.effects.items(): | ||
98 | intensities[key] = self.round_down(self.intensities[key] / other) | 98 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
99 | effects[key] = val | 99 | effects[key] = val | ||
100 | self.deprecated = True | 100 | self.deprecated = True | ||
101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | 101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) | ||
102 | 102 | ||||
103 | def __eq__(self, other): | 103 | def __eq__(self, other): | ||
104 | """Equal.""" | 104 | """Equal.""" | ||
105 | return self.intensities == other.intensities | 105 | return self.intensities == other.intensities | ||
106 | 106 | ||||
107 | def __lt__(self, other): | 107 | def __lt__(self, other): | ||
108 | """Less than.""" | 108 | """Less than.""" | ||
109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
110 | 110 | ||||
111 | 111 | ||||
112 | class ГоспожатаПоХимия: | 112 | class ГоспожатаПоХимия: | ||
113 | """Химичка Димитричка.""" | 113 | """Химичка Димитричка.""" | ||
114 | 114 | ||||
115 | def __init__(self): | 115 | def __init__(self): | ||
116 | """Initializator.""" | 116 | """Initializator.""" | ||
117 | self._cache = {} | 117 | self._cache = {} | ||
118 | 118 | ||||
119 | def _cache_target(self, target, duration): | 119 | def _cache_target(self, target, duration): | ||
120 | """Cache all public properties of a target for restoring after a given duration.""" | 120 | """Cache all public properties of a target for restoring after a given duration.""" | ||
121 | targets_cache = self._cache.setdefault(duration, {}) | 121 | targets_cache = self._cache.setdefault(duration, {}) | ||
122 | target_cache = targets_cache.setdefault(target, {}) | 122 | target_cache = targets_cache.setdefault(target, {}) | ||
123 | for key, val in vars(target).items(): | 123 | for key, val in vars(target).items(): | ||
124 | if key.startswith('_'): | 124 | if key.startswith('_'): | ||
125 | continue | 125 | continue | ||
126 | target_cache[key] = copy.copy(val) | 126 | target_cache[key] = copy.copy(val) | ||
127 | 127 | ||||
128 | def apply(self, target, potion): | 128 | def apply(self, target, potion): | ||
129 | """Apply a potion to a target.""" | 129 | """Apply a potion to a target.""" | ||
130 | sorted_effects = sorted(potion.effects.keys(), | 130 | sorted_effects = sorted(potion.effects.keys(), | ||
131 | key=lambda x: sum(map(ord, x)), | 131 | key=lambda x: sum(map(ord, x)), | ||
132 | reverse=True) | 132 | reverse=True) | ||
133 | self._cache_target(target, potion.duration) | 133 | self._cache_target(target, potion.duration) | ||
134 | for key in sorted_effects: | 134 | for key in sorted_effects: | ||
135 | try: | 135 | try: | ||
136 | getattr(potion, key)(target) | 136 | getattr(potion, key)(target) | ||
137 | except TypeError: | 137 | except TypeError: | ||
138 | pass # This effect is not available | 138 | pass # This effect is not available | ||
139 | potion.depleted = True | 139 | potion.depleted = True | ||
140 | 140 | ||||
141 | def tick(self): | 141 | def tick(self): | ||
142 | """Tick a turn.""" | 142 | """Tick a turn.""" | ||
143 | new_cache = {} | 143 | new_cache = {} | ||
144 | for dur_key, dur_val in self._cache.items(): | 144 | for dur_key, dur_val in self._cache.items(): | ||
145 | if not (ticked_dur_key := dur_key - 1): | 145 | if not (ticked_dur_key := dur_key - 1): | ||
146 | for target_key, target_val in dur_val.items(): | 146 | for target_key, target_val in dur_val.items(): | ||
147 | for attr_key, attr_val in target_val.items(): | 147 | for attr_key, attr_val in target_val.items(): | ||
148 | setattr(target_key, attr_key, attr_val) | 148 | setattr(target_key, attr_key, attr_val) | ||
149 | else: | 149 | else: | ||
150 | new_cache[ticked_dur_key] = dur_val | 150 | new_cache[ticked_dur_key] = dur_val | ||
151 | self._cache = new_cache | 151 | self._cache = new_cache |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | import copy | f | 1 | import copy |
2 | 2 | ||||
3 | 3 | ||||
4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
6 | 6 | ||||
7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
8 | """Initializator.""" | 8 | """Initializator.""" | ||
9 | self.function = function | 9 | self.function = function | ||
10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
11 | self.depleted = False | 11 | self.depleted = False | ||
12 | 12 | ||||
13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
15 | if self.depleted: | 15 | if self.depleted: | ||
16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
17 | self.depleted = True | 17 | self.depleted = True | ||
18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
19 | self.function(target) | 19 | self.function(target) | ||
20 | 20 | ||||
21 | 21 | ||||
22 | class Potion: | 22 | class Potion: | ||
23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
24 | 24 | ||||
25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
26 | """Initializator.""" | 26 | """Initializator.""" | ||
27 | self.effects = effects | 27 | self.effects = effects | ||
28 | self.duration = duration | 28 | self.duration = duration | ||
29 | self.deprecated = False | 29 | self.deprecated = False | ||
30 | self.depleted = False | 30 | self.depleted = False | ||
31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
35 | 35 | ||||
36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
38 | if object.__getattribute__(self, 'depleted'): | 38 | if object.__getattribute__(self, 'depleted'): | ||
39 | raise TypeError("Potion is depleted.") | 39 | raise TypeError("Potion is depleted.") | ||
40 | if object.__getattribute__(self, 'deprecated'): | 40 | if object.__getattribute__(self, 'deprecated'): | ||
41 | raise TypeError("Potion is now part of something bigger than itself.") | 41 | raise TypeError("Potion is now part of something bigger than itself.") | ||
42 | return object.__getattribute__(self, name) | 42 | return object.__getattribute__(self, name) | ||
43 | 43 | ||||
44 | @staticmethod | 44 | @staticmethod | ||
45 | def round_down(val): | 45 | def round_down(val): | ||
46 | """Round x.5 down, other cases - as normal.""" | 46 | """Round x.5 down, other cases - as normal.""" | ||
47 | if val % 1 <= 0.5: | 47 | if val % 1 <= 0.5: | ||
48 | return int(val) | 48 | return int(val) | ||
49 | return int(val) + 1 | 49 | return int(val) + 1 | ||
50 | 50 | ||||
51 | def __add__(self, other): | 51 | def __add__(self, other): | ||
52 | """Combinations.""" | 52 | """Combinations.""" | ||
53 | effects = {} | 53 | effects = {} | ||
54 | intensities = {} | 54 | intensities = {} | ||
55 | duration = max(self.duration, other.duration) | 55 | duration = max(self.duration, other.duration) | ||
56 | for key, val in self.effects.items(): | 56 | for key, val in self.effects.items(): | ||
57 | intensities[key] = self.intensities[key] | 57 | intensities[key] = self.intensities[key] | ||
58 | effects[key] = val | 58 | effects[key] = val | ||
59 | for key, val in other.effects.items(): | 59 | for key, val in other.effects.items(): | ||
60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
61 | effects[key] = val | 61 | effects[key] = val | ||
62 | self.deprecated = True | 62 | self.deprecated = True | ||
63 | other.deprecated = True | 63 | other.deprecated = True | ||
64 | return Potion(effects, duration, intensities) | 64 | return Potion(effects, duration, intensities) | ||
65 | 65 | ||||
66 | def __mul__(self, other): | 66 | def __mul__(self, other): | ||
67 | """Potentiation/Dilution.""" | 67 | """Potentiation/Dilution.""" | ||
68 | effects = {} | 68 | effects = {} | ||
69 | intensities = {} | 69 | intensities = {} | ||
70 | duration = self.duration | 70 | duration = self.duration | ||
71 | for key, val in self.effects.items(): | 71 | for key, val in self.effects.items(): | ||
72 | intensities[key] = self.round_down(self.intensities[key] * other) | 72 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
73 | effects[key] = val | 73 | effects[key] = val | ||
74 | self.deprecated = True | 74 | self.deprecated = True | ||
75 | return Potion(effects, duration, intensities) | 75 | return Potion(effects, duration, intensities) | ||
76 | 76 | ||||
77 | def __sub__(self, other): | 77 | def __sub__(self, other): | ||
78 | """Purification.""" | 78 | """Purification.""" | ||
79 | effects = {} | 79 | effects = {} | ||
80 | intensities = {} | 80 | intensities = {} | ||
81 | duration = self.duration | 81 | duration = self.duration | ||
82 | if set(other.effects) - set(self.effects): | 82 | if set(other.effects) - set(self.effects): | ||
83 | raise TypeError('Effect mismatch.') | 83 | raise TypeError('Effect mismatch.') | ||
84 | for key, val in self.effects.items(): | 84 | for key, val in self.effects.items(): | ||
85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | ||
86 | intensities[key] = intensity | 86 | intensities[key] = intensity | ||
87 | effects[key] = val | 87 | effects[key] = val | ||
88 | self.deprecated = True | 88 | self.deprecated = True | ||
89 | other.deprecated = True | 89 | other.deprecated = True | ||
90 | return Potion(effects, duration, intensities) | 90 | return Potion(effects, duration, intensities) | ||
91 | 91 | ||||
92 | def __truediv__(self, other): | 92 | def __truediv__(self, other): | ||
93 | """Separation.""" | 93 | """Separation.""" | ||
94 | effects = {} | 94 | effects = {} | ||
95 | intensities = {} | 95 | intensities = {} | ||
96 | duration = self.duration | 96 | duration = self.duration | ||
97 | for key, val in self.effects.items(): | 97 | for key, val in self.effects.items(): | ||
98 | intensities[key] = self.round_down(self.intensities[key] / other) | 98 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
99 | effects[key] = val | 99 | effects[key] = val | ||
100 | self.deprecated = True | 100 | self.deprecated = True | ||
t | 101 | return Potion(effects, duration, intensities) | t | 101 | return tuple([Potion(effects, duration, intensities) for _ in range(other)]) |
102 | 102 | ||||
103 | def __eq__(self, other): | 103 | def __eq__(self, other): | ||
104 | """Equal.""" | 104 | """Equal.""" | ||
105 | return self.intensities == other.intensities | 105 | return self.intensities == other.intensities | ||
106 | 106 | ||||
107 | def __lt__(self, other): | 107 | def __lt__(self, other): | ||
108 | """Less than.""" | 108 | """Less than.""" | ||
109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
110 | 110 | ||||
111 | 111 | ||||
112 | class ГоспожатаПоХимия: | 112 | class ГоспожатаПоХимия: | ||
113 | """Химичка Димитричка.""" | 113 | """Химичка Димитричка.""" | ||
114 | 114 | ||||
115 | def __init__(self): | 115 | def __init__(self): | ||
116 | """Initializator.""" | 116 | """Initializator.""" | ||
117 | self._cache = {} | 117 | self._cache = {} | ||
118 | 118 | ||||
119 | def _cache_target(self, target, duration): | 119 | def _cache_target(self, target, duration): | ||
120 | """Cache all public properties of a target for restoring after a given duration.""" | 120 | """Cache all public properties of a target for restoring after a given duration.""" | ||
121 | targets_cache = self._cache.setdefault(duration, {}) | 121 | targets_cache = self._cache.setdefault(duration, {}) | ||
122 | target_cache = targets_cache.setdefault(target, {}) | 122 | target_cache = targets_cache.setdefault(target, {}) | ||
123 | for key, val in vars(target).items(): | 123 | for key, val in vars(target).items(): | ||
124 | if key.startswith('_'): | 124 | if key.startswith('_'): | ||
125 | continue | 125 | continue | ||
126 | target_cache[key] = copy.copy(val) | 126 | target_cache[key] = copy.copy(val) | ||
127 | 127 | ||||
128 | def apply(self, target, potion): | 128 | def apply(self, target, potion): | ||
129 | """Apply a potion to a target.""" | 129 | """Apply a potion to a target.""" | ||
130 | sorted_effects = sorted(potion.effects.keys(), | 130 | sorted_effects = sorted(potion.effects.keys(), | ||
131 | key=lambda x: sum(map(ord, x)), | 131 | key=lambda x: sum(map(ord, x)), | ||
132 | reverse=True) | 132 | reverse=True) | ||
133 | self._cache_target(target, potion.duration) | 133 | self._cache_target(target, potion.duration) | ||
134 | for key in sorted_effects: | 134 | for key in sorted_effects: | ||
135 | try: | 135 | try: | ||
136 | getattr(potion, key)(target) | 136 | getattr(potion, key)(target) | ||
137 | except TypeError: | 137 | except TypeError: | ||
138 | pass # This effect is not available | 138 | pass # This effect is not available | ||
139 | potion.depleted = True | 139 | potion.depleted = True | ||
140 | 140 | ||||
141 | def tick(self): | 141 | def tick(self): | ||
142 | """Tick a turn.""" | 142 | """Tick a turn.""" | ||
143 | new_cache = {} | 143 | new_cache = {} | ||
144 | for dur_key, dur_val in self._cache.items(): | 144 | for dur_key, dur_val in self._cache.items(): | ||
145 | if not (ticked_dur_key := dur_key - 1): | 145 | if not (ticked_dur_key := dur_key - 1): | ||
146 | for target_key, target_val in dur_val.items(): | 146 | for target_key, target_val in dur_val.items(): | ||
147 | for attr_key, attr_val in target_val.items(): | 147 | for attr_key, attr_val in target_val.items(): | ||
148 | setattr(target_key, attr_key, attr_val) | 148 | setattr(target_key, attr_key, attr_val) | ||
149 | else: | 149 | else: | ||
150 | new_cache[ticked_dur_key] = dur_val | 150 | new_cache[ticked_dur_key] = dur_val | ||
151 | self._cache = new_cache | 151 | self._cache = new_cache |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | import copy | f | 1 | import copy |
2 | 2 | ||||
3 | 3 | ||||
4 | class EffectWrapper: | 4 | class EffectWrapper: | ||
5 | """Effect decorator to handle intensity and single-usage.""" | 5 | """Effect decorator to handle intensity and single-usage.""" | ||
6 | 6 | ||||
7 | def __init__(self, function, intensity): | 7 | def __init__(self, function, intensity): | ||
8 | """Initializator.""" | 8 | """Initializator.""" | ||
9 | self.function = function | 9 | self.function = function | ||
10 | self.intensity = intensity | 10 | self.intensity = intensity | ||
11 | self.depleted = False | 11 | self.depleted = False | ||
12 | 12 | ||||
13 | def __call__(self, target): | 13 | def __call__(self, target): | ||
14 | """Wrapper of the decorated function.""" | 14 | """Wrapper of the decorated function.""" | ||
15 | if self.depleted: | 15 | if self.depleted: | ||
16 | raise TypeError("Effect is depleted.") | 16 | raise TypeError("Effect is depleted.") | ||
17 | self.depleted = True | 17 | self.depleted = True | ||
18 | for _ in range(self.intensity): | 18 | for _ in range(self.intensity): | ||
19 | self.function(target) | 19 | self.function(target) | ||
20 | 20 | ||||
21 | 21 | ||||
22 | class Potion: | 22 | class Potion: | ||
23 | """Potion representation.""" | 23 | """Potion representation.""" | ||
24 | 24 | ||||
25 | def __init__(self, effects, duration, intensities=None): | 25 | def __init__(self, effects, duration, intensities=None): | ||
26 | """Initializator.""" | 26 | """Initializator.""" | ||
27 | self.effects = effects | 27 | self.effects = effects | ||
28 | self.duration = duration | 28 | self.duration = duration | ||
29 | self.deprecated = False | 29 | self.deprecated = False | ||
30 | self.depleted = False | 30 | self.depleted = False | ||
31 | self.intensities = intensities or {} | 31 | self.intensities = intensities or {} | ||
32 | for key, val in effects.items(): | 32 | for key, val in effects.items(): | ||
33 | self.intensities.setdefault(key, 1) | 33 | self.intensities.setdefault(key, 1) | ||
34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | 34 | setattr(self, key, EffectWrapper(val, self.intensities[key])) | ||
35 | 35 | ||||
36 | def __getattribute__(self, name): | 36 | def __getattribute__(self, name): | ||
37 | """Prevent usage if the potion is deprecated.""" | 37 | """Prevent usage if the potion is deprecated.""" | ||
38 | if object.__getattribute__(self, 'depleted'): | 38 | if object.__getattribute__(self, 'depleted'): | ||
39 | raise TypeError("Potion is depleted.") | 39 | raise TypeError("Potion is depleted.") | ||
40 | if object.__getattribute__(self, 'deprecated'): | 40 | if object.__getattribute__(self, 'deprecated'): | ||
41 | raise TypeError("Potion is now part of something bigger than itself.") | 41 | raise TypeError("Potion is now part of something bigger than itself.") | ||
42 | return object.__getattribute__(self, name) | 42 | return object.__getattribute__(self, name) | ||
43 | 43 | ||||
44 | @staticmethod | 44 | @staticmethod | ||
45 | def round_down(val): | 45 | def round_down(val): | ||
46 | """Round x.5 down, other cases - as normal.""" | 46 | """Round x.5 down, other cases - as normal.""" | ||
47 | if val % 1 <= 0.5: | 47 | if val % 1 <= 0.5: | ||
48 | return int(val) | 48 | return int(val) | ||
49 | return int(val) + 1 | 49 | return int(val) + 1 | ||
50 | 50 | ||||
51 | def __add__(self, other): | 51 | def __add__(self, other): | ||
52 | """Combinations.""" | 52 | """Combinations.""" | ||
53 | effects = {} | 53 | effects = {} | ||
54 | intensities = {} | 54 | intensities = {} | ||
55 | duration = max(self.duration, other.duration) | 55 | duration = max(self.duration, other.duration) | ||
56 | for key, val in self.effects.items(): | 56 | for key, val in self.effects.items(): | ||
57 | intensities[key] = self.intensities[key] | 57 | intensities[key] = self.intensities[key] | ||
58 | effects[key] = val | 58 | effects[key] = val | ||
59 | for key, val in other.effects.items(): | 59 | for key, val in other.effects.items(): | ||
60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | 60 | intensities[key] = intensities.get(key, 0) + other.intensities[key] | ||
61 | effects[key] = val | 61 | effects[key] = val | ||
62 | self.deprecated = True | 62 | self.deprecated = True | ||
63 | other.deprecated = True | 63 | other.deprecated = True | ||
64 | return Potion(effects, duration, intensities) | 64 | return Potion(effects, duration, intensities) | ||
65 | 65 | ||||
66 | def __mul__(self, other): | 66 | def __mul__(self, other): | ||
67 | """Potentiation/Dilution.""" | 67 | """Potentiation/Dilution.""" | ||
68 | effects = {} | 68 | effects = {} | ||
69 | intensities = {} | 69 | intensities = {} | ||
70 | duration = self.duration | 70 | duration = self.duration | ||
71 | for key, val in self.effects.items(): | 71 | for key, val in self.effects.items(): | ||
72 | intensities[key] = self.round_down(self.intensities[key] * other) | 72 | intensities[key] = self.round_down(self.intensities[key] * other) | ||
73 | effects[key] = val | 73 | effects[key] = val | ||
74 | self.deprecated = True | 74 | self.deprecated = True | ||
75 | return Potion(effects, duration, intensities) | 75 | return Potion(effects, duration, intensities) | ||
76 | 76 | ||||
77 | def __sub__(self, other): | 77 | def __sub__(self, other): | ||
78 | """Purification.""" | 78 | """Purification.""" | ||
79 | effects = {} | 79 | effects = {} | ||
80 | intensities = {} | 80 | intensities = {} | ||
81 | duration = self.duration | 81 | duration = self.duration | ||
82 | if set(other.effects) - set(self.effects): | 82 | if set(other.effects) - set(self.effects): | ||
83 | raise TypeError('Effect mismatch.') | 83 | raise TypeError('Effect mismatch.') | ||
84 | for key, val in self.effects.items(): | 84 | for key, val in self.effects.items(): | ||
85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | 85 | if (intensity := self.intensities[key] - other.intensities.get(key, 0)): | ||
86 | intensities[key] = intensity | 86 | intensities[key] = intensity | ||
87 | effects[key] = val | 87 | effects[key] = val | ||
88 | self.deprecated = True | 88 | self.deprecated = True | ||
89 | other.deprecated = True | 89 | other.deprecated = True | ||
90 | return Potion(effects, duration, intensities) | 90 | return Potion(effects, duration, intensities) | ||
91 | 91 | ||||
92 | def __truediv__(self, other): | 92 | def __truediv__(self, other): | ||
93 | """Separation.""" | 93 | """Separation.""" | ||
94 | effects = {} | 94 | effects = {} | ||
95 | intensities = {} | 95 | intensities = {} | ||
96 | duration = self.duration | 96 | duration = self.duration | ||
97 | for key, val in self.effects.items(): | 97 | for key, val in self.effects.items(): | ||
98 | intensities[key] = self.round_down(self.intensities[key] / other) | 98 | intensities[key] = self.round_down(self.intensities[key] / other) | ||
99 | effects[key] = val | 99 | effects[key] = val | ||
100 | self.deprecated = True | 100 | self.deprecated = True | ||
101 | return Potion(effects, duration, intensities) | 101 | return Potion(effects, duration, intensities) | ||
102 | 102 | ||||
103 | def __eq__(self, other): | 103 | def __eq__(self, other): | ||
104 | """Equal.""" | 104 | """Equal.""" | ||
105 | return self.intensities == other.intensities | 105 | return self.intensities == other.intensities | ||
106 | 106 | ||||
107 | def __lt__(self, other): | 107 | def __lt__(self, other): | ||
108 | """Less than.""" | 108 | """Less than.""" | ||
109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | 109 | return sum(self.intensities.values()) < sum(other.intensities.values()) | ||
110 | 110 | ||||
111 | 111 | ||||
112 | class ГоспожатаПоХимия: | 112 | class ГоспожатаПоХимия: | ||
113 | """Химичка Димитричка.""" | 113 | """Химичка Димитричка.""" | ||
114 | 114 | ||||
115 | def __init__(self): | 115 | def __init__(self): | ||
116 | """Initializator.""" | 116 | """Initializator.""" | ||
117 | self._cache = {} | 117 | self._cache = {} | ||
118 | 118 | ||||
119 | def _cache_target(self, target, duration): | 119 | def _cache_target(self, target, duration): | ||
120 | """Cache all public properties of a target for restoring after a given duration.""" | 120 | """Cache all public properties of a target for restoring after a given duration.""" | ||
121 | targets_cache = self._cache.setdefault(duration, {}) | 121 | targets_cache = self._cache.setdefault(duration, {}) | ||
122 | target_cache = targets_cache.setdefault(target, {}) | 122 | target_cache = targets_cache.setdefault(target, {}) | ||
123 | for key, val in vars(target).items(): | 123 | for key, val in vars(target).items(): | ||
124 | if key.startswith('_'): | 124 | if key.startswith('_'): | ||
125 | continue | 125 | continue | ||
126 | target_cache[key] = copy.copy(val) | 126 | target_cache[key] = copy.copy(val) | ||
127 | 127 | ||||
128 | def apply(self, target, potion): | 128 | def apply(self, target, potion): | ||
129 | """Apply a potion to a target.""" | 129 | """Apply a potion to a target.""" | ||
130 | sorted_effects = sorted(potion.effects.keys(), | 130 | sorted_effects = sorted(potion.effects.keys(), | ||
131 | key=lambda x: sum(map(ord, x)), | 131 | key=lambda x: sum(map(ord, x)), | ||
132 | reverse=True) | 132 | reverse=True) | ||
133 | self._cache_target(target, potion.duration) | 133 | self._cache_target(target, potion.duration) | ||
134 | for key in sorted_effects: | 134 | for key in sorted_effects: | ||
135 | try: | 135 | try: | ||
136 | getattr(potion, key)(target) | 136 | getattr(potion, key)(target) | ||
137 | except TypeError: | 137 | except TypeError: | ||
138 | pass # This effect is not available | 138 | pass # This effect is not available | ||
139 | potion.depleted = True | 139 | potion.depleted = True | ||
140 | 140 | ||||
141 | def tick(self): | 141 | def tick(self): | ||
142 | """Tick a turn.""" | 142 | """Tick a turn.""" | ||
143 | new_cache = {} | 143 | new_cache = {} | ||
144 | for dur_key, dur_val in self._cache.items(): | 144 | for dur_key, dur_val in self._cache.items(): | ||
145 | if not (ticked_dur_key := dur_key - 1): | 145 | if not (ticked_dur_key := dur_key - 1): | ||
146 | for target_key, target_val in dur_val.items(): | 146 | for target_key, target_val in dur_val.items(): | ||
147 | for attr_key, attr_val in target_val.items(): | 147 | for attr_key, attr_val in target_val.items(): | ||
148 | setattr(target_key, attr_key, attr_val) | 148 | setattr(target_key, attr_key, attr_val) | ||
149 | else: | 149 | else: | ||
150 | new_cache[ticked_dur_key] = dur_val | 150 | new_cache[ticked_dur_key] = dur_val | ||
151 | self._cache = new_cache | 151 | self._cache = new_cache | ||
t | 152 | t | |||
153 | |||||
154 | |||||
155 | |||||
156 | |||||
157 | |||||
158 | |||||
159 | |||||
160 | if __name__ == '__main__': | ||||
161 | |||||
162 | class Target: | ||||
163 | |||||
164 | def __init__(self): | ||||
165 | self.size = 5 | ||||
166 | |||||
167 | target = Target() | ||||
168 | |||||
169 | narco = ГоспожатаПоХимия() | ||||
170 | effects = {'grow': lambda target: setattr(target, 'size', target.size*2)} | ||||
171 | grow_potion1 = Potion(effects, duration=2) | ||||
172 | grow_potion2 = Potion(effects, duration=2) | ||||
173 | print(target.size) | ||||
174 | narco.apply(target, grow_potion1) | ||||
175 | print(target.size) | ||||
176 | narco.tick() | ||||
177 | print(target.size) | ||||
178 | narco.tick() | ||||
179 | print(target.size) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
04.12.2023 15:14
05.12.2023 14:34
04.12.2023 15:15