n | from collections import defaultdict | n | |
| from copy import deepcopy | | from copy import deepcopy |
| from math import ceil | | from math import ceil |
| | | |
| class Effect: | | class Effect: |
| def __init__(self, func, intensity=1): | | def __init__(self, func, intensity=1): |
n | # Avoid creating nested Effects | n | |
| self._func = func | | self._func = func |
| self.intensity = intensity | | self.intensity = intensity |
| self._depleted = False | | self._depleted = False |
| | | |
| def duplicate(self): | | def duplicate(self): |
| return Effect(self._func, self.intensity) | | return Effect(self._func, self.intensity) |
| | | |
| def __call__(self, *args,**kwargs): | | def __call__(self, *args,**kwargs): |
| if self._depleted: | | if self._depleted: |
| raise TypeError("Effect is depleted.") | | raise TypeError("Effect is depleted.") |
| else: | | else: |
| self._depleted = True | | self._depleted = True |
| for _ in range(self.intensity): | | for _ in range(self.intensity): |
| self._func(*args, **kwargs) | | self._func(*args, **kwargs) |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| return Effect(self._func, self.intensity + other.intensity) if all((self, other)) else self | | return Effect(self._func, self.intensity + other.intensity) if all((self, other)) else self |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| return Effect(self._func, self.intensity - other.intensity) if all((self, other)) else self | | return Effect(self._func, self.intensity - other.intensity) if all((self, other)) else self |
| | | |
| def __mul__(self, other): | | def __mul__(self, other): |
| return Effect(self._func, ceil(self.intensity * other - 0.5)) if all((self, other)) else self | | return Effect(self._func, ceil(self.intensity * other - 0.5)) if all((self, other)) else self |
| | | |
| def __truediv__(self, other): | | def __truediv__(self, other): |
| return Effect(self._func, ceil(self.intensity / other - 0.5)) if all((self, other)) else self | | return Effect(self._func, ceil(self.intensity / other - 0.5)) if all((self, other)) else self |
| | | |
| __radd__ = __add__ | | __radd__ = __add__ |
| __rsub__ = __sub__ | | __rsub__ = __sub__ |
| | | |
| def __eq__(self, other): | | def __eq__(self, other): |
| return self.intensity == other.intensity | | return self.intensity == other.intensity |
| | | |
| def __gt__(self, other): | | def __gt__(self, other): |
| return self.intensity > other.intensity | | return self.intensity > other.intensity |
| | | |
| | | |
| class Potion: | | class Potion: |
n | __pseudo_slots = ['duration', 'effects', 'potent', 'depleted', | n | |
| 'check_if_potent_or_depleted', 'duplicate', 'use'] | | |
| | | |
| def __init__(self, effects, duration): | | def __init__(self, effects, duration): |
| self.effects = {name: func if isinstance(func, Effect) else Effect(func) | | self.effects = {name: func if isinstance(func, Effect) else Effect(func) |
| for name, func in effects.items()} | | for name, func in effects.items()} |
| self.duration = duration | | self.duration = duration |
| self.potent = True | | self.potent = True |
| self.depleted = False | | self.depleted = False |
| | | |
| def duplicate(self): | | def duplicate(self): |
| return Potion({name: effect.duplicate() for name, effect in self.effects.items()}, | | return Potion({name: effect.duplicate() for name, effect in self.effects.items()}, |
| self.duration) | | self.duration) |
| | | |
| def check_if_potent_or_depleted(self): | | def check_if_potent_or_depleted(self): |
| if self.depleted: | | if self.depleted: |
| raise TypeError("Potion is depleted.") | | raise TypeError("Potion is depleted.") |
| if not self.potent: | | if not self.potent: |
| raise TypeError("Potion is now part of something bigger than itself.") | | raise TypeError("Potion is now part of something bigger than itself.") |
| | | |
| def use(self, target): | | def use(self, target): |
| self.check_if_potent_or_depleted() | | self.check_if_potent_or_depleted() |
| for name in sorted(self.effects.keys(), reverse=True, | | for name in sorted(self.effects.keys(), reverse=True, |
| key=lambda name: sum(ord(char) for char in name)): | | key=lambda name: sum(ord(char) for char in name)): |
| try: # This method exists becase it's not Dimitrichka's job to try/except this | | try: # This method exists becase it's not Dimitrichka's job to try/except this |
| self.effects[name](target) | | self.effects[name](target) |
| except TypeError: | | except TypeError: |
| pass # Effect is depleted | | pass # Effect is depleted |
| self.depleted = True | | self.depleted = True |
| | | |
t | def __getattribute__(self, name): | t | def __getattr__(self, name): |
| if name in Potion.__pseudo_slots: | | |
| return super().__getattribute__(name) | | |
| super().__getattribute__('check_if_potent_or_depleted')() | | super().__getattribute__('check_if_potent_or_depleted')() |
| try: | | try: |
| return super().__getattribute__('effects')[name] | | return super().__getattribute__('effects')[name] |
| except KeyError: | | except KeyError: |
| raise AttributeError | | raise AttributeError |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| self.check_if_potent_or_depleted() | | self.check_if_potent_or_depleted() |
| effects = {name: self.effects.get(name) + other.effects.get(name) | | effects = {name: self.effects.get(name) + other.effects.get(name) |
| for name in list(self.effects.keys()) + list(other.effects.keys())} | | for name in list(self.effects.keys()) + list(other.effects.keys())} |
| self.potent = False | | self.potent = False |
| other.potent = False | | other.potent = False |
| return Potion(effects, max((self.duration, other.duration))) | | return Potion(effects, max((self.duration, other.duration))) |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| self.check_if_potent_or_depleted() | | self.check_if_potent_or_depleted() |
| if not all(effect in self.effects for effect in other.effects): | | if not all(effect in self.effects for effect in other.effects): |
| raise TypeError | | raise TypeError |
| effects = {name: effect | | effects = {name: effect |
| for name in list(self.effects.keys()) + list(other.effects.keys()) | | for name in list(self.effects.keys()) + list(other.effects.keys()) |
| if (effect:=self.effects.get(name) - other.effects.get(name)).intensity > 0} | | if (effect:=self.effects.get(name) - other.effects.get(name)).intensity > 0} |
| self.potent = False | | self.potent = False |
| other.potent = False | | other.potent = False |
| return Potion(effects, self.duration) | | return Potion(effects, self.duration) |
| | | |
| def __mul__(self, multiplier): | | def __mul__(self, multiplier): |
| self.check_if_potent_or_depleted() | | self.check_if_potent_or_depleted() |
| effects = {name: self.effects[name] * multiplier for name in self.effects} | | effects = {name: self.effects[name] * multiplier for name in self.effects} |
| self.potent = False | | self.potent = False |
| return Potion(effects, self.duration) | | return Potion(effects, self.duration) |
| | | |
| def __truediv__(self, divisor): | | def __truediv__(self, divisor): |
| self.check_if_potent_or_depleted() | | self.check_if_potent_or_depleted() |
| potions = [Potion({name: self.effects[name] / divisor for name in self.effects}, | | potions = [Potion({name: self.effects[name] / divisor for name in self.effects}, |
| self.duration) | | self.duration) |
| for _ in range(divisor)] | | for _ in range(divisor)] |
| self.potent = False | | self.potent = False |
| return potions | | return potions |
| | | |
| def __eq__(self, other): | | def __eq__(self, other): |
| return self.effects.items() == other.effects.items() | | return self.effects.items() == other.effects.items() |
| | | |
| def __gt__(self, other): | | def __gt__(self, other): |
| return sum(self.effects.values()) > sum(other.effects.values()) | | return sum(self.effects.values()) > sum(other.effects.values()) |
| | | |
| | | |
| class ГоспожатаПоХимия: | | class ГоспожатаПоХимия: |
| | | |
| def __init__(self): | | def __init__(self): |
| self.target_states = {} | | self.target_states = {} |
| | | |
| def apply(self, target, potion): | | def apply(self, target, potion): |
| if target not in self.target_states: | | if target not in self.target_states: |
| self.target_states[target] = {'state': deepcopy(target.__dict__), | | self.target_states[target] = {'state': deepcopy(target.__dict__), |
| 'potions_applied': []} | | 'potions_applied': []} |
| self.target_states[target]['potions_applied'].append({'potion': potion, | | self.target_states[target]['potions_applied'].append({'potion': potion, |
| 'ticks_left': potion.duration}) | | 'ticks_left': potion.duration}) |
| potion.use(target) | | potion.use(target) |
| | | |
| def tick(self): | | def tick(self): |
| for target, target_meta in self.target_states.items(): | | for target, target_meta in self.target_states.items(): |
| # Nasty approach to solve proper state management | | # Nasty approach to solve proper state management |
| # But honestly... I'm tired of this homework | | # But honestly... I'm tired of this homework |
| target.__dict__ = deepcopy(target_meta['state']) | | target.__dict__ = deepcopy(target_meta['state']) |
| for potion_meta in target_meta['potions_applied']: | | for potion_meta in target_meta['potions_applied']: |
| potion_meta['ticks_left'] -= 1 | | potion_meta['ticks_left'] -= 1 |
| if potion_meta['ticks_left'] > 0: | | if potion_meta['ticks_left'] > 0: |
| potion_meta['potion'].duplicate().use(target) | | potion_meta['potion'].duplicate().use(target) |