| f | class Egg: | f | class Egg: |
| def __init__(self): | | def __init__(self): |
| n | self.parts = [] # (start, end, pigment) | n | self.parts = [] |
| self.filled = 0.0 | | self.filled = 0.0 |
| | | |
| self.top_broken = False | | self.top_broken = False |
| self.bottom_broken = False | | self.bottom_broken = False |
| | | |
| self.tournament = None | | self.tournament = None |
| | | |
| def paint(self, *args): | | def paint(self, *args): |
| total_to_add = 0.0 | | total_to_add = 0.0 |
| | | |
| for color, percent in args: | | for color, percent in args: |
| total_to_add += percent | | total_to_add += percent |
| | | |
| if self.filled + total_to_add > 100.0: | | if self.filled + total_to_add > 100.0: |
| raise ValueError("Too much paint") | | raise ValueError("Too much paint") |
| | | |
| current_start = self.filled | | current_start = self.filled |
| new_parts = [] | | new_parts = [] |
| | | |
| for color, percent in args: | | for color, percent in args: |
| pigment = self._get_pigment(color) | | pigment = self._get_pigment(color) |
| current_end = current_start + percent | | current_end = current_start + percent |
| new_parts.append((current_start, current_end, pigment)) | | new_parts.append((current_start, current_end, pigment)) |
| current_start = current_end | | current_start = current_end |
| | | |
| self.parts.extend(new_parts) | | self.parts.extend(new_parts) |
| self.filled += total_to_add | | self.filled += total_to_add |
| | | |
| def _get_pigment(self, hex_color): | | def _get_pigment(self, hex_color): |
| hex_color = hex_color.upper() | | hex_color = hex_color.upper() |
| | | |
| r = int(hex_color[0:2], 16) | | r = int(hex_color[0:2], 16) |
| g = int(hex_color[2:4], 16) | | g = int(hex_color[2:4], 16) |
| b = int(hex_color[4:6], 16) | | b = int(hex_color[4:6], 16) |
| | | |
| return r + g + b | | return r + g + b |
| | | |
| def _get_side_info(self, side): | | def _get_side_info(self, side): |
| if side == "top": | | if side == "top": |
| side_start = 0.0 | | side_start = 0.0 |
| side_end = 50.0 | | side_end = 50.0 |
| elif side == "bottom": | | elif side == "bottom": |
| side_start = 50.0 | | side_start = 50.0 |
| side_end = 100.0 | | side_end = 100.0 |
| else: | | else: |
| raise ValueError("Invalid side") | | raise ValueError("Invalid side") |
| | | |
| total_strength = 0.0 | | total_strength = 0.0 |
| total_coverage = 0.0 | | total_coverage = 0.0 |
| | | |
| for start, end, pigment in self.parts: | | for start, end, pigment in self.parts: |
| overlap_start = max(start, side_start) | | overlap_start = max(start, side_start) |
| overlap_end = min(end, side_end) | | overlap_end = min(end, side_end) |
| | | |
| if overlap_start < overlap_end: | | if overlap_start < overlap_end: |
| overlap_length = overlap_end - overlap_start | | overlap_length = overlap_end - overlap_start |
| total_coverage += overlap_length | | total_coverage += overlap_length |
| total_strength += pigment * (overlap_length / 50.0) | | total_strength += pigment * (overlap_length / 50.0) |
| | | |
| return total_strength, total_coverage | | return total_strength, total_coverage |
| | | |
| def _fight(self, other, side): | | def _fight(self, other, side): |
| if not isinstance(other, Egg): | | if not isinstance(other, Egg): |
| return NotImplemented | | return NotImplemented |
| | | |
| if side == "top": | | if side == "top": |
| if self.top_broken or other.top_broken: | | if self.top_broken or other.top_broken: |
| raise TypeError("One of the eggs has a broken top side") | | raise TypeError("One of the eggs has a broken top side") |
| elif side == "bottom": | | elif side == "bottom": |
| if self.bottom_broken or other.bottom_broken: | | if self.bottom_broken or other.bottom_broken: |
| raise TypeError("One of the eggs has a broken bottom side") | | raise TypeError("One of the eggs has a broken bottom side") |
| else: | | else: |
| raise ValueError("Invalid side") | | raise ValueError("Invalid side") |
| | | |
| my_strength, my_coverage = self._get_side_info(side) | | my_strength, my_coverage = self._get_side_info(side) |
| other_strength, other_coverage = other._get_side_info(side) | | other_strength, other_coverage = other._get_side_info(side) |
| | | |
| if my_strength > other_strength: | | if my_strength > other_strength: |
| winner = self | | winner = self |
| loser = other | | loser = other |
| elif other_strength > my_strength: | | elif other_strength > my_strength: |
| winner = other | | winner = other |
| loser = self | | loser = self |
| else: | | else: |
| if my_coverage > other_coverage: | | if my_coverage > other_coverage: |
| winner = self | | winner = self |
| loser = other | | loser = other |
| elif other_coverage > my_coverage: | | elif other_coverage > my_coverage: |
| winner = other | | winner = other |
| loser = self | | loser = self |
| else: | | else: |
| t | # По условие няма да има равенства, но това е безопасен fallback | t | |
| winner = self | | winner = self |
| loser = other | | loser = other |
| | | |
| if side == "top": | | if side == "top": |
| loser.top_broken = True | | loser.top_broken = True |
| else: | | else: |
| loser.bottom_broken = True | | loser.bottom_broken = True |
| | | |
| if self.tournament is not None and self.tournament is other.tournament: | | if self.tournament is not None and self.tournament is other.tournament: |
| self.tournament._record_battle(self, other, side, winner) | | self.tournament._record_battle(self, other, side, winner) |
| | | |
| return winner | | return winner |
| | | |
| def __mul__(self, other): | | def __mul__(self, other): |
| return self._fight(other, "top") | | return self._fight(other, "top") |
| | | |
| def __matmul__(self, other): | | def __matmul__(self, other): |
| return self._fight(other, "bottom") | | return self._fight(other, "bottom") |
| | | |
| | | |
| class EggTournament: | | class EggTournament: |
| def __init__(self): | | def __init__(self): |
| self.name_to_egg = {} | | self.name_to_egg = {} |
| self.egg_to_name = {} | | self.egg_to_name = {} |
| self.wins = {} | | self.wins = {} |
| | | |
| self.battles = [] | | self.battles = [] |
| | | |
| def register(self, egg, name): | | def register(self, egg, name): |
| if egg.tournament is not None: | | if egg.tournament is not None: |
| raise ValueError("An egg cannot be registered in multiple tournaments") | | raise ValueError("An egg cannot be registered in multiple tournaments") |
| | | |
| if not name.isidentifier(): | | if not name.isidentifier(): |
| raise ValueError("Invalid registration name") | | raise ValueError("Invalid registration name") |
| | | |
| if name in self.name_to_egg: | | if name in self.name_to_egg: |
| raise ValueError(f"Egg with name {name} has already been registered") | | raise ValueError(f"Egg with name {name} has already been registered") |
| | | |
| self.name_to_egg[name] = egg | | self.name_to_egg[name] = egg |
| self.egg_to_name[egg] = name | | self.egg_to_name[egg] = name |
| self.wins[egg] = 0 | | self.wins[egg] = 0 |
| egg.tournament = self | | egg.tournament = self |
| | | |
| def _record_battle(self, egg1, egg2, side, winner): | | def _record_battle(self, egg1, egg2, side, winner): |
| if egg1 not in self.egg_to_name or egg2 not in self.egg_to_name: | | if egg1 not in self.egg_to_name or egg2 not in self.egg_to_name: |
| return | | return |
| | | |
| self.battles.append({ | | self.battles.append({ |
| "egg1": egg1, | | "egg1": egg1, |
| "egg2": egg2, | | "egg2": egg2, |
| "side": side, | | "side": side, |
| "winner": winner | | "winner": winner |
| }) | | }) |
| | | |
| self.wins[winner] += 1 | | self.wins[winner] += 1 |
| | | |
| def __getitem__(self, key): | | def __getitem__(self, key): |
| if isinstance(key, tuple): | | if isinstance(key, tuple): |
| if len(key) != 3: | | if len(key) != 3: |
| raise KeyError("Invalid key") | | raise KeyError("Invalid key") |
| egg1, egg2, side = key | | egg1, egg2, side = key |
| elif isinstance(key, slice): | | elif isinstance(key, slice): |
| egg1 = key.start | | egg1 = key.start |
| egg2 = key.stop | | egg2 = key.stop |
| side = key.step | | side = key.step |
| else: | | else: |
| raise KeyError("Invalid key") | | raise KeyError("Invalid key") |
| | | |
| if side not in ("top", "bottom"): | | if side not in ("top", "bottom"): |
| raise KeyError("Invalid key") | | raise KeyError("Invalid key") |
| | | |
| for battle in reversed(self.battles): | | for battle in reversed(self.battles): |
| same_pair = ( | | same_pair = ( |
| (battle["egg1"] is egg1 and battle["egg2"] is egg2) or | | (battle["egg1"] is egg1 and battle["egg2"] is egg2) or |
| (battle["egg1"] is egg2 and battle["egg2"] is egg1) | | (battle["egg1"] is egg2 and battle["egg2"] is egg1) |
| ) | | ) |
| | | |
| if same_pair and battle["side"] == side: | | if same_pair and battle["side"] == side: |
| return battle["winner"] | | return battle["winner"] |
| | | |
| raise KeyError("No such battle") | | raise KeyError("No such battle") |
| | | |
| def _get_positions(self): | | def _get_positions(self): |
| sorted_eggs = sorted(self.wins.items(), key=lambda pair: pair[1], reverse=True) | | sorted_eggs = sorted(self.wins.items(), key=lambda pair: pair[1], reverse=True) |
| | | |
| positions = {} | | positions = {} |
| current_position = 1 | | current_position = 1 |
| | | |
| for i in range(len(sorted_eggs)): | | for i in range(len(sorted_eggs)): |
| egg, wins = sorted_eggs[i] | | egg, wins = sorted_eggs[i] |
| | | |
| if i == 0: | | if i == 0: |
| positions[egg] = current_position | | positions[egg] = current_position |
| else: | | else: |
| previous_egg, previous_wins = sorted_eggs[i - 1] | | previous_egg, previous_wins = sorted_eggs[i - 1] |
| | | |
| if wins < previous_wins: | | if wins < previous_wins: |
| current_position += 1 | | current_position += 1 |
| | | |
| positions[egg] = current_position | | positions[egg] = current_position |
| | | |
| return positions | | return positions |
| | | |
| def __rmatmul__(self, place): | | def __rmatmul__(self, place): |
| positions = self._get_positions() | | positions = self._get_positions() |
| result = set() | | result = set() |
| | | |
| for egg, position in positions.items(): | | for egg, position in positions.items(): |
| if position == place: | | if position == place: |
| result.add(egg) | | result.add(egg) |
| | | |
| if len(result) == 0: | | if len(result) == 0: |
| raise IndexError("No eggs on this position") | | raise IndexError("No eggs on this position") |
| | | |
| if len(result) == 1: | | if len(result) == 1: |
| return next(iter(result)) | | return next(iter(result)) |
| | | |
| return result | | return result |
| | | |
| def __getattr__(self, name): | | def __getattr__(self, name): |
| if name in self.name_to_egg: | | if name in self.name_to_egg: |
| egg = self.name_to_egg[name] | | egg = self.name_to_egg[name] |
| positions = self._get_positions() | | positions = self._get_positions() |
| | | |
| return { | | return { |
| "position": positions[egg], | | "position": positions[egg], |
| "victories": self.wins[egg] | | "victories": self.wins[egg] |
| } | | } |
| | | |
| raise AttributeError("Apologies, there is no such egg registered") | | raise AttributeError("Apologies, there is no such egg registered") |
| | | |
| def __contains__(self, egg): | | def __contains__(self, egg): |
| return egg in self.egg_to_name | | return egg in self.egg_to_name |
12.04.2026 21:29
12.04.2026 21:30
12.04.2026 21:41
12.04.2026 21:36
12.04.2026 21:38