1def hex_to_degit(hex_digit):
2 if hex_digit in {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}:
3 return int(hex_digit)
4 if hex_digit.lower() == "a":
5 return 10
6 if hex_digit.lower() == "b":
7 return 11
8 if hex_digit.lower() == "c":
9 return 12
10 if hex_digit.lower() == "d":
11 return 13
12 if hex_digit.lower() == "e":
13 return 14
14 if hex_digit.lower() == "f":
15 return 15
16
17def hex_to_decimal(hex_number):
18 return hex_to_degit(hex_number[0]) * 16 + hex_to_degit(hex_number[1])
19
20def hex_color_to_toughness(hex_color):
21 return hex_to_decimal(hex_color[0:2]) + hex_to_decimal(hex_color[2:4]) + hex_to_decimal(hex_color[4:6])
22
23class Egg:
24 def __init__(self):
25 self.top_colors = {}
26 self.bottom_colors = {}
27 self.is_top_broken = False
28 self.is_bottom_broken = False
29 self.tournament = None
30
31 def paint(self, *args):
32 if sum((percentage for hex_color, percentage in args)) + sum(self.top_colors.values()) + sum(self.bottom_colors.values()) > 100:
33 raise ValueError("Colored more than 100%")
34
35 for hex_color, percentage in args:
36 top_percantage = sum(self.top_colors.values())
37 if top_percantage >= 50:
38 self._add_color_to_side(hex_color, percentage, "bottom")
39 else:
40 if top_percantage + percentage <= 50:
41 self._add_color_to_side(hex_color, percentage, "top")
42 else:
43 added_to_top = 50 - top_percantage
44 self._add_color_to_side(hex_color, added_to_top, "top")
45 self._add_color_to_side(hex_color, percentage - added_to_top, "bottom")
46
47 def __mul__(self, other):
48 if self.is_top_broken or other.is_top_broken:
49 raise TypeError("The top of one of the eggs is already broken")
50
51 if not other.top_colors or self._toughness("top") > other._toughness("top"):
52 other.is_top_broken = True
53 winner = self
54 else:
55 self.is_top_broken = True
56 winner = other
57
58 self.__class__._record_victory(self, other, winner, "top")
59 return winner
60
61 def __matmul__(self, other):
62 if self.is_bottom_broken or other.is_bottom_broken:
63 raise TypeError("The bottom of one of the eggs is already broken")
64
65 if not other.bottom_colors or self._toughness("bottom") > other._toughness("bottom"):
66 other.is_bottom_broken = True
67 winner = self
68 else:
69 self.is_bottom_broken = True
70 winner = other
71
72 self.__class__._record_victory(self, other, winner, "bottom")
73 return winner
74
75 @staticmethod
76 def _record_victory(egg1, egg2, winner, side):
77 if egg1.tournament and egg2.tournament and egg1.tournament is egg2.tournament:
78 winner.tournament.history[(egg1, egg2, side)] = winner
79 winner.tournament.history[(egg2, egg1, side)] = winner
80 if winner not in winner.tournament.egg_victories:
81 winner.tournament.egg_victories[winner] = 0
82 winner.tournament.egg_victories[winner] += 1
83
84 def _add_color_to_side(self, hex_color, percentage, side):
85 if side == "top":
86 if hex_color.lower() not in self.top_colors:
87 self.top_colors[hex_color.lower()] = percentage
88 else:
89 self.top_colors[hex_color.lower()] += percentage
90 if side == "bottom":
91 if hex_color.lower() not in self.bottom_colors:
92 self.bottom_colors[hex_color.lower()] = percentage
93 else:
94 self.bottom_colors[hex_color.lower()] += percentage
95
96 def _toughness(self, side):
97 if side == "top":
98 return sum(hex_color_to_toughness(hex_color) * percentage for hex_color, percentage in self.top_colors.items())
99 if side == "bottom":
100 return sum(hex_color_to_toughness(hex_color) * percentage for hex_color, percentage in self.bottom_colors.items())
101
102
103class EggTournament:
104 def __init__(self):
105 self.eggs = {}
106 self.history = {}
107 self.egg_victories = {}
108
109 def register(self, egg, alias):
110 if not alias.isidentifier():
111 raise ValueError("Invalid registration name")
112
113 if alias in self.eggs:
114 raise ValueError(f"Egg with name {alias} has already been registered")
115
116 if egg.tournament is not None:
117 raise ValueError("An egg cannot be registered in multiple tournaments")
118
119 self.eggs[alias] = egg
120 self.egg_victories[egg] = 0
121 egg.tournament = self
122
123 def get_ranking(self):
124 eggs = sorted(self.egg_victories.items(), key=lambda x: -x[1])
125
126 ranking = {}
127 current_rank = 0
128 last_score = -1
129
130 for egg, score in eggs:
131 if score != last_score:
132 current_rank += 1
133 last_score = score
134
135 if current_rank not in ranking:
136 ranking[current_rank] = set()
137 ranking[current_rank].add(egg)
138
139 return ranking
140
141 def __rmatmul__(self, position):
142 ranking = self.get_ranking()
143
144 if position not in ranking:
145 raise IndexError("There is no such position in the ranking")
146
147 eggs = ranking[position]
148 if len(eggs) == 1:
149 return eggs.pop()
150 return eggs
151
152 def __getitem__(self, key):
153 if isinstance(key, tuple):
154 egg1, egg2, position = key
155 elif isinstance(key, slice):
156 egg1 = key.start
157 egg2 = key.stop
158 position = key.step
159 else:
160 raise KeyError("Invalid key")
161
162 if (egg1, egg2, position) in self.history:
163 return self.history[(egg1, egg2, position)]
164 raise KeyError("No clash found")
165
166 def __getattr__(self, alias):
167 if alias in self.eggs:
168 egg = self.eggs[alias]
169 ranking = self.get_ranking()
170 for position, eggs in ranking.items():
171 if egg in eggs:
172 return {
173 "position": position,
174 "victories": self.egg_victories[egg],
175 }
176 raise AttributeError("Apologies, there is no such egg registered")
177
178 def __contains__(self, egg):
179 return egg in self.eggs.values()
..............................................
----------------------------------------------------------------------
Ran 46 tests in 0.002s
OK
17.04.2026 22:25
17.04.2026 22:26
17.04.2026 22:27
17.04.2026 22:28
17.04.2026 22:28
17.04.2026 22:29