1class Egg:
2 def __init__(self):
3 self.layers = []
4 self.filled = 0.0
5 self.top_broken = False
6 self.bottom_broken = False
7 self.tournament = None
8
9 def _hex_to_value(self, hex_color):
10 hex_color = hex_color.upper()
11 r = int(hex_color[0:2], 16)
12 g = int(hex_color[2:4], 16)
13 b = int(hex_color[4:6], 16)
14 return r + g + b
15
16 def paint(self, *args):
17 total = sum(p for _, p in args)
18 if round(self.filled + total, 6) > 100:
19 raise ValueError()
20 for color, percentage in args:
21 value = self._hex_to_value(color)
22 self.layers.append((value, percentage))
23 self.filled += percentage
24
25 def _get_half_value(self, top=True):
26 target_start = 0 if top else 50
27 target_end = 50 if top else 100
28 current = 0
29 total_value = 0
30 total_coverage = 0
31 for value, perc in self.layers:
32 next_pos = current + perc
33
34 overlap_start = max(current, target_start)
35 overlap_end = min(next_pos, target_end)
36
37 if overlap_start < overlap_end:
38 portion = overlap_end - overlap_start
39 total_value += value * portion
40 total_coverage += portion
41
42 current = next_pos
43 return (total_value, total_coverage)
44
45 def _fight(self, other, top=True):
46 if top:
47 if self.top_broken or other.top_broken:
48 raise TypeError("Egg side is already broken.")
49 else:
50 if self.bottom_broken or other.bottom_broken:
51 raise TypeError("Egg side is already broken.")
52
53 my_val = self._get_half_value(top)
54 other_val = other._get_half_value(top)
55 if my_val > other_val:
56 loser = other
57 winner = self
58 else:
59 loser = self
60 winner = other
61
62 if top:
63 loser.top_broken = True
64 else:
65 loser.bottom_broken = True
66
67 if self.tournament and self.tournament == other.tournament:
68 self.tournament._record(self, other, top, winner)
69
70 return winner
71
72 def __mul__(self, other):
73 return self._fight(other, top=True)
74
75 def __matmul__(self, other):
76 return self._fight(other, top=False)
77
78
79class EggTournament:
80 def __init__(self):
81 self.eggs = {}
82 self.names = {}
83 self.victories = {}
84 self.history = []
85
86 def register(self, egg, name):
87 if not name.isidentifier():
88 raise ValueError("Invalid registration name")
89
90 if name in self.names:
91 raise ValueError(f"Egg with name {name} has already been registered")
92
93 if egg.tournament is not None:
94 raise ValueError("An egg cannot be registered in multiple tournaments")
95
96 self.eggs[egg] = name
97 self.names[name] = egg
98 self.victories[egg] = 0
99 egg.tournament = self
100
101 def _record(self, egg1, egg2, top, winner):
102 side = "top" if top else "bottom"
103 self.history.append((frozenset([egg1, egg2]), side, winner))
104 if winner in self.victories:
105 self.victories[winner] += 1
106
107 def __getitem__(self, key):
108 if isinstance(key, tuple):
109 egg1, egg2, side = key
110 else:
111 egg1, egg2, side = key.start, key.stop, key.step
112
113 for pair, s, winner in self.history:
114 if pair == frozenset([egg1, egg2]) and s == side:
115 return winner
116
117 raise KeyError()
118
119 def __rmatmul__(self, position):
120 sorted_eggs = sorted(self.victories.items(), key=lambda x: -x[1])
121
122 ranks = {}
123 current_rank = 0
124 last_score = None
125
126 for egg, score in sorted_eggs:
127 if score != last_score:
128 current_rank += 1
129 last_score = score
130 ranks.setdefault(current_rank, set()).add(egg)
131
132 if position not in ranks:
133 raise IndexError("Position out of bounds")
134
135 res = ranks[position]
136 return res if len(res) > 1 else next(iter(res))
137
138 def __getattr__(self, name):
139 names = self.__dict__.get("names", {})
140 if name not in names:
141 raise AttributeError("Apologies, there is no such egg registered")
142 egg = names[name]
143 victories = self.__dict__.get("victories", {})
144 wins = victories.get(egg, 0)
145 sorted_scores = sorted(set(victories.values()), reverse=True)
146 position = sorted_scores.index(wins) + 1
147 return {"position": position, "victories": wins}
148
149 def __contains__(self, egg):
150 return egg in self.eggs
..............................................
----------------------------------------------------------------------
Ran 46 tests in 0.001s
OK
15.04.2026 16:23
15.04.2026 16:24
15.04.2026 16:24
15.04.2026 16:26