1from collections import defaultdict
2import keyword
3
4class Egg:
5
6 def __init__(self):
7 self._percentage_painted = 0.0
8 self._top_broken = False
9 self._bottom_broken = False
10 self._color = []
11 self.tournament = None
12
13 def paint(self, *args):
14
15 total = sum(percentage for _, percentage in args)
16 if round(total + self.percentage_painted, 5) > 100.0:
17 raise ValueError("Total percentage cannot exceed 100%")
18
19 for hex_color, percentage in args:
20 color = hex_color.upper()
21
22 start = self._percentage_painted
23 end = self._percentage_painted + percentage
24
25 if start < 50.0 and end > 50.0:
26 self._color.append((color, 50.0 - start))
27 self._color.append((color, end - 50.0))
28 else:
29 self._color.append((color, percentage))
30
31 self._percentage_painted += percentage
32
33 def calculate_color_strength(self, color):
34 r = int(color[0:2], 16)
35 g = int(color[2:4], 16)
36 b = int(color[4:6], 16)
37 return r + g + b
38
39 @property
40 def percentage_painted(self):
41 return self._percentage_painted
42
43 @property
44 def colored_upper_side_strength(self):
45 strength = 0.0
46 total = 0.0
47 for color, percentage in self._color:
48 if total < 50.0:
49 strength += self.calculate_color_strength(color) * (percentage / 50.0)
50 total += percentage
51 return strength if total > 0.0 else -1.0
52
53 @property
54 def colored_lower_side_strength(self):
55 #calculate the strength of the second 50% of the egg
56 strength = 0.0
57 total = 0.0
58 for color, percentage in self._color:
59 if total < 50.0:
60 total += percentage
61 continue
62
63 strength += self.calculate_color_strength(color) * (percentage / 50.0)
64 total += percentage
65 return strength if total > 50.0 else -1.0
66
67 def __mul__(self, other):
68 if not isinstance(other, Egg):
69 raise ValueError("Egg can only hit another Egg")
70
71 if self._top_broken or other._top_broken:
72 raise TypeError("Cannot hit with a broken top")
73
74 if self.colored_upper_side_strength > other.colored_upper_side_strength:
75 other._top_broken = True
76 if self.tournament is not None and self.tournament is other.tournament:
77 self.tournament.record_match(self, other, "top")
78 return self
79 else:
80 self._top_broken = True
81 if self.tournament is not None and self.tournament is other.tournament:
82 self.tournament.record_match(other, self, "top")
83 return other
84
85 def __matmul__(self, other):
86 if not isinstance(other, Egg):
87 raise ValueError("Egg can only hit another Egg")
88
89 if self._bottom_broken or other._bottom_broken:
90 raise TypeError("Cannot hit with a broken bottom")
91
92 if self.colored_lower_side_strength > other.colored_lower_side_strength:
93 other._bottom_broken = True
94 if self.tournament is not None and self.tournament is other.tournament:
95 self.tournament.record_match(self, other, "bottom")
96 return self
97 else:
98 self._bottom_broken = True
99 if self.tournament is not None and self.tournament is other.tournament:
100 self.tournament.record_match(other, self, "bottom")
101 return other
102
103
104
105class EggTournament:
106 def __init__(self):
107 self._participants = {}
108 self._history = {}
109
110 def register(self, egg, egg_name):
111 if not isinstance(egg, Egg):
112 raise ValueError("Only Eggs can participate in the tournament")
113
114 if egg.tournament is not None:
115 raise ValueError("An egg cannot be registered in multiple tournaments")
116
117 if not egg_name.isidentifier() or keyword.iskeyword(egg_name):
118 raise ValueError("Invalid registration name")
119
120 if egg_name in self._participants:
121 raise ValueError(f"Egg with name {egg_name} has already been registered")
122
123 self._participants[egg_name] = egg
124 egg.tournament = self
125
126 def __getitem__(self, item):
127 if isinstance(item, tuple) and len(item) == 3:
128 egg1, egg2, type = item
129 elif isinstance(item, slice):
130 egg1 = item.start
131 egg2 = item.stop
132 type = item.step
133 else:
134 raise KeyError("Invalid input format.")
135
136 key = (frozenset([egg1, egg2]), type)
137 if key not in self._history:
138 raise KeyError("No match history for the given eggs and type.")
139 return self._history[key]
140
141 def record_match(self, winner, loser, type):
142 key = (frozenset([winner, loser]), type)
143 self._history[key] = winner
144
145 def __rmatmul__(self, rank):
146 if not isinstance(rank, int) or rank < 1:
147 raise ValueError("Rank must be a positive integer")
148
149 wins = {egg: 0 for egg in self._participants.values()}
150 for winner in self._history.values():
151 if winner in wins:
152 wins[winner] += 1
153
154 win_groups = {}
155 for egg, win_count in wins.items():
156 if win_count not in win_groups:
157 win_groups[win_count] = set()
158 win_groups[win_count].add(egg)
159
160 sorted_win_counts = sorted(win_groups.keys(), reverse=True)
161 if rank > len(sorted_win_counts):
162 raise IndexError("Rank not found in the tournament")
163
164 eggs_at_rank = win_groups[sorted_win_counts[rank - 1]]
165 if len(eggs_at_rank) == 1:
166 return next(iter(eggs_at_rank))
167 else:
168 return eggs_at_rank
169
170 def __getattr__(self, name):
171 if name not in self._participants:
172 raise AttributeError("Apologies, there is no such egg registered")
173
174 target = self._participants[name]
175 wins = {egg: 0 for egg in self._participants.values()}
176 for winner in self._history.values():
177 if winner in wins:
178 wins[winner] += 1
179
180 victories = wins[target]
181 unique_wins = sorted(list(set(wins.values())), reverse=True)
182 pos = unique_wins.index(victories) + 1
183
184 return {"position" : pos, "victories": victories}
185
186 def __contains__(self, egg):
187 return egg in self._participants.values()
..............................................
----------------------------------------------------------------------
Ran 46 tests in 0.001s
OK
16.04.2026 18:35
16.04.2026 18:36
16.04.2026 18:37
16.04.2026 18:38