1class Egg:
2 def __init__(self):
3 self.layers = [] # [(value, percent)]
4 self.total_percent = 0.0
5 self.broken_top = False
6 self.broken_bottom = False
7 self.tournament = None
8
9 def _hex_to_value(self, hex_color):
10 r = int(hex_color[0:2], 16)
11 g = int(hex_color[2:4], 16)
12 b = int(hex_color[4:6], 16)
13 return r + g + b
14
15 def paint(self, *args):
16 add = sum(p for _, p in args)
17
18 if self.total_percent + add > 100:
19 raise ValueError("Overpainting not allowed")
20
21 for color, percent in args:
22 value = self._hex_to_value(color.upper())
23 self.layers.append((value, percent))
24 self.total_percent += percent
25
26 def _strength(self, part):
27 if part == "top":
28 start, end = 0, 50
29 else:
30 start, end = 50, 100
31
32 cur = 0
33 strength = 0
34
35 for value, percent in self.layers:
36 nxt = cur + percent
37
38 overlap_start = max(cur, start)
39 overlap_end = min(nxt, end)
40
41 if overlap_start < overlap_end:
42 overlap = overlap_end - overlap_start
43 strength += value * (overlap / 100)
44
45 cur = nxt
46
47 return strength
48
49 def _fight(self, other, part):
50 if part == "top":
51 if self.broken_top or other.broken_top:
52 raise TypeError("Broken egg side")
53 else:
54 if self.broken_bottom or other.broken_bottom:
55 raise TypeError("Broken egg side")
56
57 my_strength = self._strength(part)
58 other_strength = other._strength(part)
59
60 if my_strength >= other_strength:
61 winner, loser = self, other
62 else:
63 winner, loser = other, self
64
65 if part == "top":
66 loser.broken_top = True
67 else:
68 loser.broken_bottom = True
69
70 if self.tournament is not None and self.tournament is other.tournament:
71 self.tournament.record(self, other, part, winner)
72
73 return winner
74
75 def __mul__(self, other):
76 return self._fight(other, "top")
77
78 def __matmul__(self, other):
79 return self._fight(other, "bottom")
80
81
82class EggTournament:
83 def __init__(self):
84 self.eggs = {} # egg -> name
85 self.names = {} # name -> egg
86 self.history = {} # (e1, e2, part) -> winner
87 self.wins = {}
88
89 def register(self, egg, name):
90 if not name.isidentifier():
91 raise ValueError("Invalid name")
92
93 if egg.tournament is not None:
94 raise ValueError("Egg already in tournament")
95
96 if name in self.names:
97 raise ValueError("Name already used")
98
99 self.eggs[egg] = name
100 self.names[name] = egg
101 self.wins[egg] = 0
102 egg.tournament = self
103
104 def record(self, e1, e2, part, winner):
105 if e1.tournament is not self or e2.tournament is not self:
106 return
107
108 name1 = self.eggs[e1]
109 name2 = self.eggs[e2]
110
111 key = (tuple(sorted((e1, e2), key=lambda e: self.eggs[e])), part)
112
113 self.history[key] = winner
114 self.wins[winner] += 1
115
116 def __getitem__(self, key):
117 if isinstance(key, tuple):
118 e1, e2, part = key
119 else:
120 e1, e2, part = key.start, key.stop, key.step
121
122 real_key = (
123 tuple(sorted((e1, e2), key=lambda e: self.eggs[e])),
124 part
125 )
126
127 return self.history[real_key]
128
129 def _ranking(self):
130 sorted_eggs = sorted(
131 self.wins.items(),
132 key=lambda x: (-x[1], id(x[0]))
133 )
134
135 ranking = {}
136 pos = 1
137 prev = None
138
139 for i, (egg, wins) in enumerate(sorted_eggs):
140 if wins != prev:
141 pos = i + 1
142 ranking.setdefault(pos, set()).add(egg)
143 prev = wins
144
145 return ranking
146
147 def __rmatmul__(self, position):
148 ranking = self._ranking()
149
150 if position not in ranking:
151 raise IndexError
152
153 res = ranking[position]
154 return next(iter(res)) if len(res) == 1 else res
155
156 def __getattr__(self, name):
157 if name not in self.names:
158 raise AttributeError("Apologies, there is no such egg registered.")
159
160 egg = self.names[name]
161 ranking = self._ranking()
162
163 for pos, eggs in ranking.items():
164 if egg in eggs:
165 return {
166 "position": pos,
167 "victories": self.wins[egg]
168 }
169
170 def __contains__(self, egg):
171 return egg in self.eggs
.....................F.......F.......FF..EFF..
======================================================================
ERROR: test_ranking_uses_dense_ranking_without_skipping_places (test.TestEggTournament.test_ranking_uses_dense_ranking_without_skipping_places)
Looking up ranking positions should use dense ranking without skipping places.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 516, in test_ranking_uses_dense_ranking_without_skipping_places
self.assertEqual(3 @ tournament, {delta, epsilon})
~~^~~~~~~~~~~~
File "/tmp/solution.py", line 151, in __rmatmul__
raise IndexError
IndexError
======================================================================
FAIL: test_unpainted_half_loses_to_half_painted_black (test.TestEgg.test_unpainted_half_loses_to_half_painted_black)
An unpainted half should lose to a half painted in black.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 200, in test_unpainted_half_loses_to_half_painted_black
self.assertIs(unpainted_egg * black_egg, black_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: <solution.Egg object at 0x7593f7140de0> is not <solution.Egg object at 0x7593f7140ec0>
======================================================================
FAIL: test_egg_cannot_be_registered_in_second_tournament (test.TestEggTournament.test_egg_cannot_be_registered_in_second_tournament)
Registering an egg in a second tournament should raise a ValueError.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 344, in test_egg_cannot_be_registered_in_second_tournament
self.assertEqual(
~~~~~~~~~~~~~~~~^
str(context.exception),
^^^^^^^^^^^^^^^^^^^^^^^
"An egg cannot be registered in multiple tournaments",
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
AssertionError: 'Egg already in tournament' != 'An egg cannot be registered in multiple tournaments'
- Egg already in tournament
+ An egg cannot be registered in multiple tournaments
======================================================================
FAIL: test_missing_egg_attribute_raises_attribute_error (test.TestEggTournament.test_missing_egg_attribute_raises_attribute_error)
Accessing an unregistered egg as an attribute should raise an AttributeError.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 586, in test_missing_egg_attribute_raises_attribute_error
self.assertEqual(
~~~~~~~~~~~~~~~~^
str(context.exception),
^^^^^^^^^^^^^^^^^^^^^^^
"Apologies, there is no such egg registered",
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
AssertionError: 'Apologies, there is no such egg registered.' != 'Apologies, there is no such egg registered'
- Apologies, there is no such egg registered.
? -
+ Apologies, there is no such egg registered
======================================================================
FAIL: test_ranking_missing_position_raises_index_error (test.TestEggTournament.test_ranking_missing_position_raises_index_error)
Looking up a missing ranking position should raise an IndexError.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 538, in test_ranking_missing_position_raises_index_error
with self.assertRaises(IndexError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^
AssertionError: IndexError not raised
======================================================================
FAIL: test_register_duplicate_name_raises_value_error (test.TestEggTournament.test_register_duplicate_name_raises_value_error)
Registering an egg with a duplicate name should raise a ValueError.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 328, in test_register_duplicate_name_raises_value_error
self.assertEqual(
~~~~~~~~~~~~~~~~^
str(context.exception),
^^^^^^^^^^^^^^^^^^^^^^^
"Egg with name the_monster has already been registered",
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
AssertionError: 'Name already used' != 'Egg with name the_monster has already been registered'
- Name already used
+ Egg with name the_monster has already been registered
======================================================================
FAIL: test_register_invalid_name_raises_value_error (test.TestEggTournament.test_register_invalid_name_raises_value_error)
Registering an egg with an invalid name should raise a ValueError.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 315, in test_register_invalid_name_raises_value_error
self.assertEqual(str(context.exception), "Invalid registration name")
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 'Invalid name' != 'Invalid registration name'
- Invalid name
+ Invalid registration name
----------------------------------------------------------------------
Ran 46 tests in 0.004s
FAILED (failures=6, errors=1)
Виктор Бечев
12.04.2026 21:45Браво. Тук-таме има имена, които в друга ситуация бих поставил под въпрос, но истината е, че в случая всичко се чете. И най-приятното е, че си използвала на правилните места питонски идиоми.
|
12.04.2026 21:49