Домашни > Работилница за отвари! > Решения > Решението на Владимир Великов

Резултати
8 точки от тестове
1 точки от учител

9 точки общо

17 успешни теста
3 неуспешни теста
Код
Скрий всички коментари

  1import math as m
  2from copy import deepcopy
  3
  4class Effect:
  5    def __init__(self, effect, intensity=1):
  6        self.counter = 0
  7        self.intensity = intensity
  8        self.effect = effect
  9        
 10    def __call__(self, *args, **kwargs):
 11        if self.counter >= 1:
 12            raise TypeError('Effect is depleted.')
 13        else:
 14            self.counter += 1
 15            for _ in range(self.intensity):
 16                self.effect(*args, *kwargs)
 17            self.intensity = 0
 18                
 19    def __add__(self, other):
 20        return Effect(self.effect, self.intensity + other.intensity)
 21                
 22    def __mul__(self, k):
 23        power = self.intensity*k
 24        if k % 1:
 25            power = m.floor(power) if power % 1 <= 0.5 else round(power)
 26        return Effect(self.effect, power)
 27    
 28    def __sub__(self, other):
 29        intensity = self.intensity - other.intensity
 30        if intensity <= 0:
 31            return None
 32        return Effect(self.effect, self.intensity - other.intensity)
 33    
 34    def __truediv__(self, k):
 35        power = self.intensity/k
 36        power = m.floor(power) if power % 1 <= 0.5 else round(power)
 37        return Effect(self.effect, power)
 38         
 39
 40class Potion:
 41    def __init__(self, effects, duration, *args):
 42        self.effects = {}
 43        for name, func in effects.items():
 44            effect = Effect(func)
 45            if type(func) != Effect:
 46                setattr(self, name, effect)
 47                self.effects[name] = effect
 48            else:
 49                setattr(self, name, func)
 50                self.effects[name] = func
 51        if args:
 52            for name, effect in args[0].items():
 53                updated_effect = getattr(self, name) + effect
 54                setattr(self, name, updated_effect)
 55                self.effects[name] = updated_effect
 56        self.duration = duration
 57        self.__expired = False
 58        self.counter = 0
 59        
 60    def __has_expired(self):
 61        if self.counter >= 1:
 62            raise TypeError('Potion is depleted.')
 63        if self.__expired:
 64            raise TypeError('Potion is now part of something bigger than itself.')
 65        self.__expired = True
 66        
 67    def __add__(self, other):
 68        self.__has_expired()
 69        other.__has_expired()
 70        return Potion(self.effects|other.effects, 
 71                      max(self.duration, other.duration), 
 72                      {k: e for k, e in self.effects.items() if k in other.effects})
 73        
 74    def __call__(self, *args, **kwargs):
 75        self.__has_expired()
 76        self.counter += 1
 77        def cmp(pair):
 78            name, _ = pair
 79            return sum(ord(ch) for ch in name)
 80        for name , effect in sorted(self.effects.items(), key=cmp, reverse=True):
 81            try:
 82                effect(*args, **kwargs)
 83            except TypeError:
 84                continue
 85        
 86    def __mul__(self, intensity):
 87        self.__has_expired()
 88        return Potion({n: e*intensity for n, e in self.effects.items()}, self.duration)
 89            
 90    def __sub__(self, other):
 91        self.__has_expired()
 92        other.__has_expired()
 93        if len(self.effects) != len(self.effects|other.effects):
 94            raise TypeError
 95        
 96        updated_effects = {k: e for k, e in self.effects.items()}
 97        for name, effect in other.effects.items():
 98                updated_effects[name] -= effect
 99                if updated_effects[name] == None:
100                    updated_effects.pop(name)
101        return Potion(updated_effects, self.duration)
102        
103    def __truediv__(self, n):
104        self.__has_expired()
105        potions = []
106        for _ in range(n):
107            potions.append(Potion({k: e/n for k, e in self.effects.items()}, self.duration))
108        return tuple(potions)
109    
110    def __eq__(self, other):
111        for name, effect in self.effects.items():
112            if name in other.effects:
113                if effect.intensity != other.effects[name].intensity:
114                    return False
115            else:
116                return False
117        return True
118    
119    def __ne__(self, other):
120        return not self == other
121    
122    def __gt__(self, other):
123        return sum([e.intensity for e in self.effects.values()]) > sum([e.intensity for e in other.effects.values()])
124    
125    def __lt__(self, other):
126        return other > self
127
128
129class ГоспожатаПоХимия:
130    def __init__(self):
131        self.victims = []
132        self.timer = 0
133    
134    def apply(self, target, potion):
135        target_copy = deepcopy(target)
136        setattr(target, 'prev', target_copy)
137        potion(target)
138        self.victims.append((target, potion))
139        
140    def tick(self):
141        self.timer += 1
142        for victim, potion in self.victims:
143            if potion.duration == self.timer:
144                for attr in [attr for attr in dir(victim.prev) if attr.startswith('__') is False]:
145                    setattr(victim, attr, getattr(victim.prev, attr))

...F...F.........F..
======================================================================
FAIL: test_equal (test.TestPotionComparison)
Test equality of potions.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 304, in test_equal
self.assertNotEqual(potion1, potion2)
AssertionError: <solution.Potion object at 0x7fe25f207370> == <solution.Potion object at 0x7fe25f207100>

======================================================================
FAIL: test_deprecation (test.TestPotionOperations)
Test deprecation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 257, in test_deprecation
with self.assertRaisesRegex(TypeError, 'Potion is now part of something bigger than itself\.'):
AssertionError: TypeError not raised

======================================================================
FAIL: test_ticking_multiple_potions (test.TestГоспожатаПоХимия)
Test ticking after applying multiple potions which affect the same attribute.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 471, in test_ticking_multiple_potions
self.assertEqual(self._target.int_attr, 5)
AssertionError: 50 != 5

----------------------------------------------------------------------
Ran 20 tests in 0.002s

FAILED (failures=3)

Дискусия
Виктор Бечев
05.12.2023 13:54

Единствената ми забележка са еднобуквените имена. В сравнително прост код и малки функции, както в случая - преживява се, имената са дефинирани или на същия ред или 1-2 реда по-нагоре. Но е хубава практика да се избягват, много е лесно да го направиш нечетимо, да вкараш колизии на имена и прочие.
История
Това решение има само една версия.