1class Egg:
2 HALF_PAINTED_LIMIT = 50.0
3 FULL_PAINTED_LIMIT = 100.0
4
5 def __init__(self):
6 self.color_counter = 0.0
7 self._is_top_painted = False
8 self._is_bottom_painted = False
9 self.top_half = []
10 self.bottom_half = []
11 self.is_registered = False
12 self.is_top_broken = False
13 self.is_bottom_broken = False
14 self.tournament = None
15
16 def __mul__(self, other):
17 if not isinstance(other, Egg):
18 raise TypeError("Parameter is not of type Egg")
19 if other.is_top_broken or self.is_top_broken:
20 raise TypeError("At least one of the eggs' top side is broken")
21 if self.__get_half_painted(self.top_half) > self.__get_half_painted(other.top_half):
22 other.is_top_broken = True
23 winner = self
24 else:
25 self.is_top_broken = True
26 winner = other
27 if self.tournament is not None and self.tournament is other.tournament:
28 self.tournament.save_fight(self, other, "top", winner)
29 return winner
30
31 def __matmul__(self, other):
32 if not isinstance(other, Egg):
33 raise TypeError("Parameter is not of type Egg")
34 if other.is_bottom_broken or self.is_bottom_broken:
35 raise TypeError("At least one of the eggs' bottom side is broken")
36 if self.__get_half_painted(self.bottom_half) > self.__get_half_painted(other.bottom_half):
37 other.is_bottom_broken = True
38 winner = self
39 else:
40 self.is_bottom_broken = True
41 winner = other
42 if self.tournament is not None and self.tournament is other.tournament:
43 self.tournament.save_fight(self, other, "bottom", winner)
44 return winner
45
46 def __get_half_painted(self, half):
47 egg_strength = 0
48 painted_percentage = 0
49 for (hex_color, percentage) in half:
50 half_painted_percentage = percentage / self.HALF_PAINTED_LIMIT
51 egg_strength += Egg.convert_to_decimal(hex_color) * half_painted_percentage
52 painted_percentage += half_painted_percentage
53 return egg_strength, painted_percentage
54
55 @staticmethod
56 def convert_to_decimal(color):
57 r = int(color[0:2], 16)
58 g = int(color[2:4], 16)
59 b = int(color[4:6], 16)
60 return r + g + b
61
62 def paint(self, *colors):
63 if self.color_counter + sum(percentage for (_, percentage) in colors) > self.FULL_PAINTED_LIMIT:
64 raise ValueError("The paint on this egg exceeds 100%.")
65 for (hex_color, percentage) in colors:
66 color = hex_color.upper()
67 if self.color_counter + percentage <= self.HALF_PAINTED_LIMIT:
68 self.top_half.append((color, percentage))
69 elif self.color_counter + percentage > self.HALF_PAINTED_LIMIT and not self._is_top_painted:
70 needed_percentage = self.HALF_PAINTED_LIMIT - self.color_counter
71 part_for_top_half = min(percentage, needed_percentage)
72 part_for_bottom_half = percentage - part_for_top_half
73 self.top_half.append((color, part_for_top_half))
74 self.bottom_half.append((color, part_for_bottom_half))
75 elif self.color_counter + percentage <= self.FULL_PAINTED_LIMIT:
76 self.bottom_half.append((color, percentage))
77 if self.color_counter + percentage >= self.HALF_PAINTED_LIMIT:
78 self._is_top_painted = True
79 if self.color_counter + percentage >= self.FULL_PAINTED_LIMIT:
80 self._is_bottom_painted = True
81 self.color_counter += percentage
82
83
84class EggTournament:
85 def __init__(self):
86 self.registered_eggs = {}
87 self.fight_history = {}
88
89 def __getitem__(self, item):
90 if isinstance(item, tuple):
91 first_egg, second_egg, fight_side = item
92 elif isinstance(item, slice):
93 first_egg = item.start
94 second_egg = item.stop
95 fight_side = item.step
96 else:
97 raise TypeError("Parameter is not of type tuple or slice")
98 if fight_side != "bottom" and fight_side != "top":
99 raise KeyError(f"Invalid fight side {fight_side}")
100 return self.fight_history[(frozenset((first_egg, second_egg)), fight_side)]
101
102 def __contains__(self, egg):
103 return egg in self.registered_eggs.values()
104
105 def __getattr__(self, egg_name):
106 if egg_name not in self.registered_eggs:
107 raise AttributeError("Apologies, there is no such egg registered")
108 return {
109 "position": self._get_position(self.registered_eggs[egg_name]),
110 "victories": self._get_victories(self.registered_eggs[egg_name])
111 }
112
113 def __rmatmul__(self, position):
114 sorted_victories = self.__sort_by_victories()
115 ranking = list(sorted_victories.keys())
116 if position < 1 or position > len(ranking):
117 raise IndexError("The position is out of range")
118 if len(sorted_victories[ranking[position - 1]]) == 1:
119 return sorted_victories[ranking[position - 1]][0]
120 return set(sorted_victories[ranking[position - 1]])
121
122 def __sort_by_victories(self):
123 victories_by_egg = {}
124 for egg in self.registered_eggs.values():
125 victories = self._get_victories(egg)
126 if victories in victories_by_egg:
127 victories_by_egg[victories].append(egg)
128 else:
129 victories_by_egg[victories] = [egg]
130 return dict(sorted(victories_by_egg.items(), reverse = True))
131
132 def _get_victories(self, egg):
133 victories = 0
134 for winner in self.fight_history.values():
135 if winner is egg:
136 victories += 1
137 return victories
138
139 def _get_position(self, egg):
140 sorted_victories = self.__sort_by_victories()
141 ranking = list(sorted_victories.keys())
142 return ranking.index(self._get_victories(egg)) + 1
143
144 def save_fight(self, first_egg, second_egg, fight_side, winner):
145 self.fight_history[(frozenset((first_egg, second_egg)), fight_side)] = winner
146
147 def register(self, egg, name):
148 if egg.is_registered == True and egg.tournament is not None:
149 raise ValueError("An egg cannot be registered in multiple tournaments")
150 if not name.isidentifier():
151 raise ValueError("Invalid registration name")
152 if name in self.registered_eggs:
153 raise ValueError(f"Egg with name {name} has already been registered")
154 self.registered_eggs[name] = egg
155 egg.is_registered = True
156 egg.tournament = self
..............................................
----------------------------------------------------------------------
Ran 46 tests in 0.001s
OK
16.04.2026 18:40
16.04.2026 18:42
16.04.2026 18:43
16.04.2026 18:44
16.04.2026 18:48
16.04.2026 18:50