Домашни > Работилница за отвари! > Решения > Решението на Георги Кунчев

Резултати
10 точки от тестове
0 точки от учител

10 точки общо

20 успешни теста
0 неуспешни теста
Код (Ensure Potion.intensities matches EffectWrappers)
Скрий всички коментари

  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

Дискусия
История

f1import copyf1import copy
22
33
4class EffectWrapper:4class EffectWrapper:
5    """Effect decorator to handle intensity and single-usage."""5    """Effect decorator to handle intensity and single-usage."""
66
7    def __init__(self, function, intensity):7    def __init__(self, function, intensity):
8        """Initializator."""8        """Initializator."""
9        self.function = function9        self.function = function
10        self.intensity = intensity10        self.intensity = intensity
11        self.depleted = False11        self.depleted = False
1212
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 = True17        self.depleted = True
18        for _ in range(self.intensity):18        for _ in range(self.intensity):
19            self.function(target)19            self.function(target)
2020
2121
22class Potion:22class Potion:
23    """Potion representation."""23    """Potion representation."""
2424
25    def __init__(self, effects, duration, intensities=None):25    def __init__(self, effects, duration, intensities=None):
26        """Initializator."""26        """Initializator."""
27        self.effects = effects27        self.effects = effects
28        self.duration = duration28        self.duration = duration
29        self.deprecated = False29        self.deprecated = False
30        self.depleted = False30        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]))
3535
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.")
nn43            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 available46            pass # deepcopy starts using this code before depleted and deprecated are available
45        return object.__getattribute__(self, name)47        return object.__getattribute__(self, name)
4648
47    @staticmethod49    @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) + 154        return int(val) + 1
5355
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] = val63            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] = val66            effects[key] = val
65        self.deprecated = True67        self.deprecated = True
66        other.deprecated = True68        other.deprecated = True
67        return Potion(effects, duration, intensities)69        return Potion(effects, duration, intensities)
6870
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.duration75        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] = val78            effects[key] = val
77        self.deprecated = True79        self.deprecated = True
78        return Potion(effects, duration, intensities)80        return Potion(effects, duration, intensities)
7981
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.duration86        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] = intensity91                intensities[key] = intensity
90                effects[key] = val92                effects[key] = val
91        self.deprecated = True93        self.deprecated = True
92        other.deprecated = True94        other.deprecated = True
93        return Potion(effects, duration, intensities)95        return Potion(effects, duration, intensities)
9496
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.duration101        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] = val104            effects[key] = val
103        self.deprecated = True105        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)])
105107
106    def __eq__(self, other):108    def __eq__(self, other):
107        """Equal."""109        """Equal."""
108        return self.intensities == other.intensities110        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())
113115
114116
115class ГоспожатаПоХимия:117class ГоспожатаПоХимия:
116    """Химичка Димитричка."""118    """Химичка Димитричка."""
117119
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 False128            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                continue132                continue
131            self._target_cache[target][key] = copy.deepcopy(val)133            self._target_cache[target][key] = copy.deepcopy(val)
132134
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 available145                pass # This effect is not available
144        potion.depleted = True146        potion.depleted = True
145        return potion_copy147        return potion_copy
146148
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 state151        # 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 potions154        # Apply all active potions
153        # and replace the used potion with a potion ready to be used again155        # 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_copy159                self._potions_stack[target][i] = potion_copy
158160
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 attribute163        # Ensure potion is not depleted by trying to get an attribute
162        # Depleted potion will raise a TypeError if it is depleted164        # Depleted potion will raise a TypeError if it is depleted
163        potion.depleted165        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 -= 1175                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_stack178            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}
181183
tt184 
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)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import copyf1import copy
22
33
4class EffectWrapper:4class EffectWrapper:
5    """Effect decorator to handle intensity and single-usage."""5    """Effect decorator to handle intensity and single-usage."""
66
7    def __init__(self, function, intensity):7    def __init__(self, function, intensity):
8        """Initializator."""8        """Initializator."""
9        self.function = function9        self.function = function
10        self.intensity = intensity10        self.intensity = intensity
11        self.depleted = False11        self.depleted = False
1212
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 = True17        self.depleted = True
18        for _ in range(self.intensity):18        for _ in range(self.intensity):
19            self.function(target)19            self.function(target)
2020
2121
22class Potion:22class Potion:
23    """Potion representation."""23    """Potion representation."""
2424
25    def __init__(self, effects, duration, intensities=None):25    def __init__(self, effects, duration, intensities=None):
26        """Initializator."""26        """Initializator."""
27        self.effects = effects27        self.effects = effects
28        self.duration = duration28        self.duration = duration
29        self.deprecated = False29        self.deprecated = False
30        self.depleted = False30        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]))
3535
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."""
nn38        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)
4346
44    @staticmethod47    @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) + 152        return int(val) + 1
5053
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] = val61            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] = val64            effects[key] = val
62        self.deprecated = True65        self.deprecated = True
63        other.deprecated = True66        other.deprecated = True
64        return Potion(effects, duration, intensities)67        return Potion(effects, duration, intensities)
6568
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.duration73        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] = val76            effects[key] = val
74        self.deprecated = True77        self.deprecated = True
75        return Potion(effects, duration, intensities)78        return Potion(effects, duration, intensities)
7679
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.duration84        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] = intensity89                intensities[key] = intensity
87                effects[key] = val90                effects[key] = val
88        self.deprecated = True91        self.deprecated = True
89        other.deprecated = True92        other.deprecated = True
90        return Potion(effects, duration, intensities)93        return Potion(effects, duration, intensities)
9194
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.duration99        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] = val102            effects[key] = val
100        self.deprecated = True103        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)])
102105
103    def __eq__(self, other):106    def __eq__(self, other):
104        """Equal."""107        """Equal."""
105        return self.intensities == other.intensities108        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())
110113
111114
112class ГоспожатаПоХимия:115class ГоспожатаПоХимия:
113    """Химичка Димитричка."""116    """Химичка Димитричка."""
114117
115    def __init__(self):118    def __init__(self):
116        """Initializator."""119        """Initializator."""
n117        self._cache = {}n120        self._target_cache = {}
121        self._potions_stack = {}
118    122    
n119    def _cache_target(self, target, duration):n123    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                continue130                continue
n126            target_cache[key] = copy.copy(val)n131            self._target_cache[target][key] = copy.deepcopy(val)
127132
n128    def apply(self, target, potion):n133    def _apply_potion(self, target, potion):
129        """Apply a potion to a target."""134        """Apply a potion to a target."""
nn135        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)
n133        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 available143                pass # This effect is not available
139        potion.depleted = True144        potion.depleted = True
nn145        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."""
t143        new_cache = {}t170        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_val177            self._apply_all_potions(target)
151        self._cache = new_cache178            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
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import copyf1import copy
22
33
4class EffectWrapper:4class EffectWrapper:
5    """Effect decorator to handle intensity and single-usage."""5    """Effect decorator to handle intensity and single-usage."""
66
7    def __init__(self, function, intensity):7    def __init__(self, function, intensity):
8        """Initializator."""8        """Initializator."""
9        self.function = function9        self.function = function
10        self.intensity = intensity10        self.intensity = intensity
11        self.depleted = False11        self.depleted = False
1212
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 = True17        self.depleted = True
18        for _ in range(self.intensity):18        for _ in range(self.intensity):
19            self.function(target)19            self.function(target)
2020
2121
22class Potion:22class Potion:
23    """Potion representation."""23    """Potion representation."""
2424
25    def __init__(self, effects, duration, intensities=None):25    def __init__(self, effects, duration, intensities=None):
26        """Initializator."""26        """Initializator."""
27        self.effects = effects27        self.effects = effects
28        self.duration = duration28        self.duration = duration
29        self.deprecated = False29        self.deprecated = False
30        self.depleted = False30        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]))
3535
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)
4343
44    @staticmethod44    @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) + 149        return int(val) + 1
5050
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] = val58            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] = val61            effects[key] = val
62        self.deprecated = True62        self.deprecated = True
63        other.deprecated = True63        other.deprecated = True
64        return Potion(effects, duration, intensities)64        return Potion(effects, duration, intensities)
6565
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.duration70        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] = val73            effects[key] = val
74        self.deprecated = True74        self.deprecated = True
75        return Potion(effects, duration, intensities)75        return Potion(effects, duration, intensities)
7676
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.duration81        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():
t85            if (intensity := self.intensities[key] - other.intensities.get(key, 0)):t85            if (intensity := self.intensities[key] - other.intensities.get(key, 0)) > 0:
86                intensities[key] = intensity86                intensities[key] = intensity
87                effects[key] = val87                effects[key] = val
88        self.deprecated = True88        self.deprecated = True
89        other.deprecated = True89        other.deprecated = True
90        return Potion(effects, duration, intensities)90        return Potion(effects, duration, intensities)
9191
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.duration96        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] = val99            effects[key] = val
100        self.deprecated = True100        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)])
102102
103    def __eq__(self, other):103    def __eq__(self, other):
104        """Equal."""104        """Equal."""
105        return self.intensities == other.intensities105        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())
110110
111111
112class ГоспожатаПоХимия:112class ГоспожатаПоХимия:
113    """Химичка Димитричка."""113    """Химичка Димитричка."""
114114
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                continue125                continue
126            target_cache[key] = copy.copy(val)126            target_cache[key] = copy.copy(val)
127127
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 available138                pass # This effect is not available
139        potion.depleted = True139        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_val150                new_cache[ticked_dur_key] = dur_val
151        self._cache = new_cache151        self._cache = new_cache
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import copyf1import copy
22
33
4class EffectWrapper:4class EffectWrapper:
5    """Effect decorator to handle intensity and single-usage."""5    """Effect decorator to handle intensity and single-usage."""
66
7    def __init__(self, function, intensity):7    def __init__(self, function, intensity):
8        """Initializator."""8        """Initializator."""
9        self.function = function9        self.function = function
10        self.intensity = intensity10        self.intensity = intensity
11        self.depleted = False11        self.depleted = False
1212
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 = True17        self.depleted = True
18        for _ in range(self.intensity):18        for _ in range(self.intensity):
19            self.function(target)19            self.function(target)
2020
2121
22class Potion:22class Potion:
23    """Potion representation."""23    """Potion representation."""
2424
25    def __init__(self, effects, duration, intensities=None):25    def __init__(self, effects, duration, intensities=None):
26        """Initializator."""26        """Initializator."""
27        self.effects = effects27        self.effects = effects
28        self.duration = duration28        self.duration = duration
29        self.deprecated = False29        self.deprecated = False
30        self.depleted = False30        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]))
3535
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)
4343
44    @staticmethod44    @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) + 149        return int(val) + 1
5050
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] = val58            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] = val61            effects[key] = val
62        self.deprecated = True62        self.deprecated = True
63        other.deprecated = True63        other.deprecated = True
64        return Potion(effects, duration, intensities)64        return Potion(effects, duration, intensities)
6565
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.duration70        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] = val73            effects[key] = val
74        self.deprecated = True74        self.deprecated = True
75        return Potion(effects, duration, intensities)75        return Potion(effects, duration, intensities)
7676
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.duration81        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] = intensity86                intensities[key] = intensity
87                effects[key] = val87                effects[key] = val
88        self.deprecated = True88        self.deprecated = True
89        other.deprecated = True89        other.deprecated = True
90        return Potion(effects, duration, intensities)90        return Potion(effects, duration, intensities)
9191
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.duration96        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] = val99            effects[key] = val
100        self.deprecated = True100        self.deprecated = True
t101        return Potion(effects, duration, intensities)t101        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.intensities105        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())
110110
111111
112class ГоспожатаПоХимия:112class ГоспожатаПоХимия:
113    """Химичка Димитричка."""113    """Химичка Димитричка."""
114114
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                continue125                continue
126            target_cache[key] = copy.copy(val)126            target_cache[key] = copy.copy(val)
127127
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 available138                pass # This effect is not available
139        potion.depleted = True139        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_val150                new_cache[ticked_dur_key] = dur_val
151        self._cache = new_cache151        self._cache = new_cache
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import copyf1import copy
22
33
4class EffectWrapper:4class EffectWrapper:
5    """Effect decorator to handle intensity and single-usage."""5    """Effect decorator to handle intensity and single-usage."""
66
7    def __init__(self, function, intensity):7    def __init__(self, function, intensity):
8        """Initializator."""8        """Initializator."""
9        self.function = function9        self.function = function
10        self.intensity = intensity10        self.intensity = intensity
11        self.depleted = False11        self.depleted = False
1212
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 = True17        self.depleted = True
18        for _ in range(self.intensity):18        for _ in range(self.intensity):
19            self.function(target)19            self.function(target)
2020
2121
22class Potion:22class Potion:
23    """Potion representation."""23    """Potion representation."""
2424
25    def __init__(self, effects, duration, intensities=None):25    def __init__(self, effects, duration, intensities=None):
26        """Initializator."""26        """Initializator."""
27        self.effects = effects27        self.effects = effects
28        self.duration = duration28        self.duration = duration
29        self.deprecated = False29        self.deprecated = False
30        self.depleted = False30        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]))
3535
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)
4343
44    @staticmethod44    @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) + 149        return int(val) + 1
5050
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] = val58            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] = val61            effects[key] = val
62        self.deprecated = True62        self.deprecated = True
63        other.deprecated = True63        other.deprecated = True
64        return Potion(effects, duration, intensities)64        return Potion(effects, duration, intensities)
6565
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.duration70        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] = val73            effects[key] = val
74        self.deprecated = True74        self.deprecated = True
75        return Potion(effects, duration, intensities)75        return Potion(effects, duration, intensities)
7676
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.duration81        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] = intensity86                intensities[key] = intensity
87                effects[key] = val87                effects[key] = val
88        self.deprecated = True88        self.deprecated = True
89        other.deprecated = True89        other.deprecated = True
90        return Potion(effects, duration, intensities)90        return Potion(effects, duration, intensities)
9191
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.duration96        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] = val99            effects[key] = val
100        self.deprecated = True100        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.intensities105        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())
110110
111111
112class ГоспожатаПоХимия:112class ГоспожатаПоХимия:
113    """Химичка Димитричка."""113    """Химичка Димитричка."""
114114
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                continue125                continue
126            target_cache[key] = copy.copy(val)126            target_cache[key] = copy.copy(val)
127127
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 available138                pass # This effect is not available
139        potion.depleted = True139        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_val150                new_cache[ticked_dur_key] = dur_val
151        self._cache = new_cache151        self._cache = new_cache
t152 t
153 
154 
155 
156 
157 
158 
159 
160if __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
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op