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

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

8 точки общо

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

  1def multiple_calls_wrapper(func, count):
  2    def wrapper(*args, **kwargs):
  3        for _ in range(count):
  4            func(*args, **kwargs)
  5    return wrapper
  6
  7custom_round = lambda x: int(x) + 1 if x % 1 > 0.5 else int(x)
  8
  9class Potion:
 10    def __init__(self, effects, duration):
 11        self.effects = effects
 12        self.intensities = {effect : 1 for effect in self.effects.keys()}
 13        self.depleted_effects = set()
 14        self.is_depleted = False
 15        self.is_applied = False
 16        self.duration = duration
 17
 18    def __getattr__(self, name):
 19        if self.is_depleted:
 20            raise TypeError("Potion is now part of something bigger than itself.")
 21        elif self.is_applied or name in self.effects and name in self.depleted_effects:
 22            raise TypeError("Effect is depleted.")
 23        elif name in self.effects and name not in self.depleted_effects:
 24            self.depleted_effects.add(name)
 25            self.is_applied = self.depleted_effects == set(self.effects)
 26            return multiple_calls_wrapper(self.effects[name], self.intensities[name])
 27        else: 
 28            raise AttributeError(f"Couldn't find {name} attribute")
 29
 30    def __add__(self, other_potion):
 31        if self.is_depleted or other_potion.is_depleted:
 32            raise TypeError("Potion is now part of something bigger than itself.")
 33        if self.is_applied or other_potion.is_applied:
 34            raise TypeError("Potion is depleted.")
 35        self.is_depleted = True
 36        other_potion.is_depleted = True
 37
 38        new_duration = max(self.duration, other_potion.duration)
 39        new_effects = {**self.effects, **other_potion.effects}
 40        #union of intensities, where intensities are summed for common effects  
 41        new_intensities = {key : self.intensities.get(key) for key in set(self.intensities.keys()) - self.depleted_effects}
 42        for key in set(other_potion.intensities.keys()) - other_potion.depleted_effects:
 43            if key in new_intensities:
 44                new_intensities[key] += other_potion.intensities[key]
 45            else:
 46                new_intensities[key] = other_potion.intensities[key]
 47
 48        result = Potion(new_effects, new_duration)
 49        result.intensities = new_intensities
 50        return result
 51    
 52    def __mul__(self, times):
 53        if self.is_depleted:
 54            raise TypeError("Potion is now part of something bigger than itself.")
 55        if self.is_applied:
 56            raise TypeError("Potion is depleted.")
 57        self.is_depleted = True
 58
 59        #dict comprehension with custom rounding criteria 
 60        #(>x.5 is rounded to x + 1; <=x.5 is rounded to x)
 61        new_intensities = {key: custom_round(times * val)
 62                            for (key, val) in self.intensities.items()}
 63        result = Potion(self.effects, self.duration)
 64        result.intensities = new_intensities
 65        result.depleted_effects = self.depleted_effects
 66        return result
 67    
 68    
 69    def __rmul__(self, times):
 70        return self.__mul__(times)
 71    
 72    def __imul__(self, times):
 73        self = self.__mul__(times)
 74        return self
 75    
 76    def __sub__(self, other_potion):
 77        if self.is_depleted or other_potion.is_depleted:
 78            raise TypeError("Potion is now part of something bigger than itself.")
 79        if self.is_applied or other_potion.is_applied:
 80            raise TypeError("Potion is depleted.")
 81        self.is_depleted = True
 82        other_potion.is_depleted = True
 83
 84        if  not (set(other_potion.effects.keys()) <= set(self.effects.keys())):
 85            raise TypeError("Invalid subtract")
 86        
 87        new_intensities = self.intensities
 88        for effect in other_potion.intensities:
 89            new_intensities[effect] -= other_potion.intensities[effect]
 90            if new_intensities[effect] < 0:
 91                del new_intensities[effect]
 92        new_effects = {key : val for (key, val) in self.effects.items() if key in new_intensities}
 93        result = Potion(new_effects, self.duration)
 94        result.intensities = new_intensities
 95        result.depleted_effects = self.depleted_effects & set(new_effects.keys())
 96        return result
 97    
 98    def __truediv__(self, times):
 99        if self.is_depleted:
100             raise TypeError("Potion is now part of something bigger than itself.")
101        if self.is_applied:
102            raise TypeError("Potion is depleted.")
103        self.is_depleted = True
104
105        new_intensities = {key : custom_round(val / times) for (key, val) in self.intensities.items()}
106        result = Potion(self.effects, self.duration)
107        result.intensities = new_intensities
108        return tuple([result for _ in range(times)])
109    
110    def __eq__(self, other_potion):
111        first_effects = set(self.intensities) - set(self.depleted_effects)
112        second_effects = set(other_potion.intensities) - set(other_potion.depleted_effects)
113        if first_effects != second_effects:
114            return False
115        for effect in first_effects:
116            if other_potion.intensities[effect] != self.intensities[effect]:
117                return False
118        return True
119    
120    def __gt__(self, other_potion):
121        first_sum = sum([self.intensities[effect] for effect in self.effects if effect not in self.depleted_effects])
122        second_sum = sum([other_potion.intensities[effect] for effect in other_potion.effects
123                           if effect not in other_potion.depleted_effects])
124        return first_sum > second_sum
125
126class ГоспожатаПоХимия:
127    def __init__(self):
128        self.history = {}
129    def apply(self, target, potion, save = True):
130        if potion.is_applied:
131            raise TypeError("Potion is depleted.")
132        if potion.is_depleted:
133             raise TypeError("Potion is now part of something bigger than itself.")
134        
135        if save:
136            if not id(target) in self.history.keys():
137                self.history[id(target)] = [target, target.__dict__.copy()]
138            self.history[id(target)].extend([potion.__dict__.copy(), potion.duration])
139
140        inorder_effects = [effect for effect in potion.effects.keys() if not effect in potion.depleted_effects]
141        inorder_effects.sort(key = lambda x : sum([ord(ch) for ch in x]), reverse = True)
142        for effect in inorder_effects:
143            potion.__getattr__(effect)(target)
144
145        potion.is_applied = True
146
147    def reapply_potions(self, new_list):
148        cur_obj = new_list[0]
149        cur_obj.__dict__ = new_list[1]
150        for i in range(2, len(new_list), 2):
151            cur_potion = Potion(None, None)
152            cur_potion.__dict__ = new_list[i]
153            self.apply(cur_obj, cur_potion, False)
154        
155
156    def tick(self):
157        for key in self.history.keys():
158            reapply = False
159            new_list = []
160
161            for i in range(len(self.history[key])):
162                if not isinstance(self.history[key][i], int):
163                    new_list.append(self.history[key][i])
164                    continue
165                self.history[key][i] -= 1
166                if self.history[key][i] == 0:
167                    reapply = True
168                    new_list.pop()
169                    continue
170                new_list.append(self.history[key][i])
171
172            self.history[key] = new_list
173            if reapply:
174                self.reapply_potions(new_list)

..........FE.....E.F
======================================================================
ERROR: test_separation (test.TestPotionOperations)
Test separation of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 218, in test_separation
potion2.int_attr_fun(self._target)
File "/tmp/solution.py", line 22, in __getattr__
raise TypeError("Effect is depleted.")
TypeError: Effect is depleted.

======================================================================
ERROR: 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 457, in test_ticking_multiple_potions
self._dimitrichka.tick()
File "/tmp/solution.py", line 174, in tick
self.reapply_potions(new_list)
File "/tmp/solution.py", line 151, in reapply_potions
cur_potion = Potion(None, None)
File "/tmp/solution.py", line 12, in __init__
self.intensities = {effect : 1 for effect in self.effects.keys()}
AttributeError: 'NoneType' object has no attribute 'keys'

======================================================================
FAIL: test_purification (test.TestPotionOperations)
Test purification of a potion.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 168, in test_purification
with self.assertRaises(AttributeError):
AssertionError: AttributeError not raised

======================================================================
FAIL: test_ticking_mutable (test.TestГоспожатаПоХимия)
Test ticking after applying a potion with mutable attributes.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 446, in test_ticking_mutable
self.assertEqual(self._target.list_attr, [1, 2, 3])
AssertionError: Lists differ: [1, 2, 3, 4] != [1, 2, 3]

First list contains 1 additional elements.
First extra element 3:
4

- [1, 2, 3, 4]
? ---

+ [1, 2, 3]

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

FAILED (failures=2, errors=2)

Дискусия
История
Това решение има само една версия.