1from collections import defaultdict
2
3class Egg:
4 __MAX_SPACE = 50.0
5 __BASE_HARDNESS = 0.0
6
7 def __init__(self):
8 self.tournament = None
9 self.victories_in_tournament = 0
10 self.battles_history = []
11 self.top_hardness = Egg.__BASE_HARDNESS
12 self.bottom_hardness = Egg.__BASE_HARDNESS
13 self.__colors = []
14 self.__top_not_painted_space = Egg.__MAX_SPACE
15 self.__bottom_not_painted_space = Egg.__MAX_SPACE
16
17 def lost(self, which_half):
18 setattr(self, which_half, None)
19
20 def __to_numeric_rgb(self, color):
21 return tuple(int(color[i:i+2].upper(), 16) for i in (0, 2, 4))
22
23 def __give_hardness(self, which_half, *color):
24 if which_half == "top":
25 self.top_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper()))
26 elif which_half == "bottom":
27 self.bottom_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper()))
28
29 def paint(self, *colors):
30 for color in colors:
31 if self.__top_not_painted_space + self.__bottom_not_painted_space < color[1]:
32 raise ValueError("Not enough space to paint the egg with the given color.")
33 elif self.__top_not_painted_space >= color[1]:
34 self.__give_hardness("top", color[0].upper(), color[1])
35 self.__top_not_painted_space -= color[1]
36 self.__colors.append(color)
37 elif self.__top_not_painted_space < color[1]:
38 self.__give_hardness("top", color[0].upper(), self.__top_not_painted_space)
39 self.__give_hardness("bottom", color[0].upper(), color[1] - self.__top_not_painted_space)
40 self.__top_not_painted_space = 0.0
41 self.__bottom_not_painted_space -= (color[1] - self.__top_not_painted_space)
42 self.__colors.append(color)
43 elif self.__top_not_painted_space == 0.0:
44 self.__give_hardness("bottom", color[0].upper(), color[1])
45 self.__bottom_not_painted_space -= color[1]
46 self.__colors.append(color)
47
48 def __add_battle(self, opponent, which_half, result):
49 self.battles_history.append((opponent, which_half, result))
50 if result == "win":
51 self.victories_in_tournament += 1
52
53 def return_battle_winner(self, opponent, which_half):
54 for battle in self.battles_history:
55 if battle[0] is opponent and battle[1] == which_half:
56 return self if battle[2] == "win" else opponent
57 else:
58 return None
59
60 def __mul__(self, other):
61 if not isinstance(other, Egg):
62 raise ValueError("Can only multiply with another Egg instance.")
63
64 if self.top_hardness is None or other.top_hardness is None:
65 raise TypeError("Спукана работа :(")
66
67 if self.top_hardness > other.top_hardness:
68 if self.tournament is other.tournament and self.tournament:
69 self.__add_battle(other, "top", "win")
70 other.__add_battle(self, "top", "lose")
71 other.lost("top_hardness")
72 elif self.top_hardness < other.top_hardness:
73 if self.tournament is other.tournament and self.tournament:
74 self.__add_battle(other, "top", "lose")
75 other.__add_battle(self, "top", "win")
76 self.lost("top_hardness")
77 elif self.top_hardness == 0.0:
78 if self.__top_not_painted_space < self.__MAX_SPACE and other.__top_not_painted_space == self.__MAX_SPACE:
79 if self.tournament is other.tournament and self.tournament:
80 self.__add_battle(other, "top", "win")
81 other.__add_battle(self, "top", "lose")
82 other.lost("top_hardness")
83 elif self.__top_not_painted_space == self.__MAX_SPACE and other.__top_not_painted_space < self.__MAX_SPACE:
84 if self.tournament is other.tournament and self.tournament:
85 self.__add_battle(other, "top", "lose")
86 other.__add_battle(self, "top", "win")
87 self.lost("top_hardness")
88
89 def __matmul__(self, other):
90 if not isinstance(other, Egg):
91 raise ValueError("Can only multiply with another Egg instance.")
92
93 if self.bottom_hardness is None or other.bottom_hardness is None:
94 raise TypeError("Спукана работа :(")
95
96 if self.bottom_hardness > other.bottom_hardness:
97 if self.tournament is other.tournament and self.tournament:
98 self.__add_battle(other, "bottom", "win")
99 other.__add_battle(self, "bottom", "lose")
100 other.lost("bottom_hardness")
101 elif self.bottom_hardness < other.bottom_hardness:
102 if self.tournament is other.tournament and self.tournament:
103 self.__add_battle(other, "bottom", "lose")
104 other.__add_battle(self, "bottom", "win")
105 self.lost("bottom_hardness")
106 elif self.bottom_hardness == 0.0:
107 if self.__bottom_not_painted_space < self.__MAX_SPACE and other.__bottom_not_painted_space == self.__MAX_SPACE:
108 if self.tournament is other.tournament and self.tournament:
109 self.__add_battle(other, "bottom", "win")
110 other.__add_battle(self, "bottom", "lose")
111 other.lost("bottom_hardness")
112 elif self.__bottom_not_painted_space == self.__MAX_SPACE and other.__bottom_not_painted_space < self.__MAX_SPACE:
113 if self.tournament is other.tournament and self.tournament:
114 self.__add_battle(other, "bottom", "lose")
115 other.__add_battle(self, "bottom", "win")
116 self.lost("bottom_hardness")
117
118
119class EggTournament:
120
121 def __init__(self):
122 self.__registered_eggs = {}
123
124 @property
125 def __ranking(self):
126 grouped_by_victories = defaultdict(list)
127 for name, egg in self.__registered_eggs.items():
128 grouped_by_victories[egg.victories_in_tournament].append(name)
129
130 sorted_victories = sorted(grouped_by_victories.keys(), reverse=True)
131 result = {}
132 position = 1
133 for victories in sorted_victories:
134 result[position] = grouped_by_victories[victories]
135 position += 1
136 return result
137
138 def register(self, egg, name):
139 if any(name == reg_egg_name for reg_egg_name in self.__registered_eggs.keys()):
140 raise ValueError(f"Egg with name {name} has already been registered")
141 elif egg.tournament:
142 raise ValueError(f"An egg cannot be registered in multiple tournaments")
143 elif not name.isidentifier():
144 raise ValueError(f"Invalid registration name")
145 else:
146 self.__registered_eggs[name] = egg
147 egg.tournament = self
148
149 def __getitem__(self, key):
150 if isinstance(key, slice):
151 egg1, egg2, which_half = key.start, key.stop, key.step
152 else:
153 egg1, egg2, which_half = key
154 if egg1 not in self.__registered_eggs.values() or egg2 not in self.__registered_eggs.values():
155 raise KeyError("There is no such battle in the tournament")
156 if egg1.return_battle_winner(egg2, which_half) is None:
157 raise KeyError("There is no such battle in the tournament")
158 return egg1.return_battle_winner(egg2, which_half)
159
160 def __rmatmul__(self, position):
161 if position in self.__ranking.keys():
162 return {self.__registered_eggs[name] for name in self.__ranking[position]}
163 else:
164 raise IndexError("There is no such position in the ranking table")
165
166 def __getattr__(self, name):
167 for pos in self.__ranking.keys():
168 if name in self.__ranking[pos]:
169 rank = pos
170 break
171 else:
172 raise AttributeError("Apologies, there is no such egg registered")
173 return {"position": rank, "victories": self.__registered_eggs[name].victories_in_tournament} if name in self.__registered_eggs else AttributeError(f"Apologies, there is no such egg registered")
174
175 def __contains__(self, egg):
176 return egg in self.__registered_eggs.values()
FFFFFFFFFEEFEF...FFFFFFFFF..F..F.FFFF.FFFF..FF
======================================================================
ERROR: test_paint_chunk_crossing_50_percent_boundary_is_split_correctly (test.TestEgg.test_paint_chunk_crossing_50_percent_boundary_is_split_correctly)
Painting a chunk across the half boundary should split it correctly between the two halves.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 135, in test_paint_chunk_crossing_50_percent_boundary_is_split_correctly
egg = self.make_egg(("FFFFFF", 40.0), ("000000", 20.0), ("FF0000", 40.0))
File "/tmp/test.py", line 11, in make_egg
egg.paint(*paint_chunks)
~~~~~~~~~^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 32, in paint
raise ValueError("Not enough space to paint the egg with the given color.")
ValueError: Not enough space to paint the egg with the given color.
======================================================================
ERROR: test_paint_exactly_to_100_percent_is_allowed (test.TestEgg.test_paint_exactly_to_100_percent_is_allowed)
Painting an egg exactly to 100 percent should not raise an error.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 37, in test_paint_exactly_to_100_percent_is_allowed
egg.paint(("FFFFFF", 1.0))
~~~~~~~~~^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 32, in paint
raise ValueError("Not enough space to paint the egg with the given color.")
ValueError: Not enough space to paint the egg with the given color.
======================================================================
ERROR: test_paint_multiple_calls_can_cross_50_percent_boundary (test.TestEgg.test_paint_multiple_calls_can_cross_50_percent_boundary)
Painting an egg across the half boundary in multiple calls should split the halves correctly.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 146, in test_paint_multiple_calls_can_cross_50_percent_boundary
egg.paint(("FF0000", 40.0))
~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 32, in paint
raise ValueError("Not enough space to paint the egg with the given color.")
ValueError: Not enough space to paint the egg with the given color.
======================================================================
FAIL: test_bottom_collision_returns_egg_with_greater_bottom_strength (test.TestEgg.test_bottom_collision_returns_egg_with_greater_bottom_strength)
A bottom collision should return the egg with the greater bottom strength.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 177, in test_bottom_collision_returns_egg_with_greater_bottom_strength
self.assertIs(stronger_egg @ weaker_egg, stronger_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f852ba0>
======================================================================
FAIL: test_breaking_one_side_does_not_prevent_using_the_other_side (test.TestEgg.test_breaking_one_side_does_not_prevent_using_the_other_side)
Breaking one side of an egg should not prevent using the other side.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 287, in test_breaking_one_side_does_not_prevent_using_the_other_side
self.assertIs(first_egg * second_egg, second_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f82f490>
======================================================================
FAIL: test_broken_side_raises_type_error_regardless_of_operand_position (test.TestEgg.test_broken_side_raises_type_error_regardless_of_operand_position)
Using a broken side in a collision should raise a TypeError regardless of operand position.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 257, in test_broken_side_raises_type_error_regardless_of_operand_position
self.assertIs(broken_top_egg * top_winner, top_winner)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f680950>
======================================================================
FAIL: test_collision_result_is_independent_of_operand_order (test.TestEgg.test_collision_result_is_independent_of_operand_order)
Swapping the egg operands should not change the collision winner.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 212, in test_collision_result_is_independent_of_operand_order
self.assertIs(top_first_egg * top_second_egg, top_first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f7fb9b0>
======================================================================
FAIL: test_collision_with_partially_painted_eggs (test.TestEgg.test_collision_with_partially_painted_eggs)
Partially painted eggs should collide correctly from both sides.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 192, in test_collision_with_partially_painted_eggs
self.assertIs(first_egg * second_egg, first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f83da50>
======================================================================
FAIL: test_failed_paint_overflow_does_not_change_egg_state (test.TestEgg.test_failed_paint_overflow_does_not_change_egg_state)
Failing to paint an egg because of overflow should not change its state.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 91, in test_failed_paint_overflow_does_not_change_egg_state
self.assertIs(reference_egg * reference_top_opponent, reference_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f7d2990>
======================================================================
FAIL: test_losing_side_becomes_unusable (test.TestEgg.test_losing_side_becomes_unusable)
Losing a collision should make the losing side unusable.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 238, in test_losing_side_becomes_unusable
self.assertIs(top_loser * top_winner, top_winner)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f89d490>
======================================================================
FAIL: test_paint_boundary_exactly_at_50_percent (test.TestEgg.test_paint_boundary_exactly_at_50_percent)
Painting an egg exactly to the half boundary should keep the two halves separate.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 130, in test_paint_boundary_exactly_at_50_percent
self.assertIs(egg * opponent, egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6749f0>
======================================================================
FAIL: test_paint_can_be_called_multiple_times (test.TestEgg.test_paint_can_be_called_multiple_times)
Painting an egg in multiple calls should preserve the full painting order.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 54, in test_paint_can_be_called_multiple_times
self.assertIs(egg * top_opponent, egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6749f0>
======================================================================
FAIL: test_paint_is_case_insensitive_for_hex_colors (test.TestEgg.test_paint_is_case_insensitive_for_hex_colors)
Painting an egg with lowercase and uppercase hex colors should produce the same behavior.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 111, in test_paint_is_case_insensitive_for_hex_colors
self.assertIs(lower_case_egg * lower_case_top_opponent, lower_case_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6749f0>
======================================================================
FAIL: test_paint_order_of_color_chunks_matters (test.TestEgg.test_paint_order_of_color_chunks_matters)
Painting the same colors in different orders should change the collision result.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 122, in test_paint_order_of_color_chunks_matters
self.assertIs(first_egg * second_egg, first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f674e10>
======================================================================
FAIL: test_paint_overflow_with_multiple_colors_should_not_change_the_egg (test.TestEgg.test_paint_overflow_with_multiple_colors_should_not_change_the_egg)
Failing to overpaint an egg with multiple colors should not change the egg.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 163, in test_paint_overflow_with_multiple_colors_should_not_change_the_egg
self.assertIs(egg @ bottom_opponent, bottom_opponent)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f674c00>
======================================================================
FAIL: test_paint_single_color_full_egg (test.TestEgg.test_paint_single_color_full_egg)
A fully painted single-color egg should win collisions against a weaker egg from both sides.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 30, in test_paint_single_color_full_egg
self.assertIs(egg * opponent, egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6750d0>
======================================================================
FAIL: test_same_two_eggs_can_have_different_winners_for_top_and_bottom (test.TestEgg.test_same_two_eggs_can_have_different_winners_for_top_and_bottom)
The same two eggs should be able to have different winners for top and bottom collisions.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 184, in test_same_two_eggs_can_have_different_winners_for_top_and_bottom
self.assertIs(first_egg * second_egg, second_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6750d0>
======================================================================
FAIL: test_top_collision_returns_egg_with_greater_top_strength (test.TestEgg.test_top_collision_returns_egg_with_greater_top_strength)
A top collision should return the egg with the greater top strength.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 170, in test_top_collision_returns_egg_with_greater_top_strength
self.assertIs(stronger_egg * weaker_egg, stronger_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f674f70>
======================================================================
FAIL: test_unpainted_half_loses_to_half_painted_black (test.TestEgg.test_unpainted_half_loses_to_half_painted_black)
An unpainted half should lose to a half painted in black.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 200, in test_unpainted_half_loses_to_half_painted_black
self.assertIs(unpainted_egg * black_egg, black_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6747e0>
======================================================================
FAIL: test_winner_side_does_not_become_broken (test.TestEgg.test_winner_side_does_not_become_broken)
Winning a collision should not break the winning side.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 302, in test_winner_side_does_not_become_broken
self.assertIs(winner_egg * first_loser, winner_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6750d0>
======================================================================
FAIL: test_collision_between_registered_and_unregistered_egg_is_not_recorded (test.TestEggTournament.test_collision_between_registered_and_unregistered_egg_is_not_recorded)
A collision between a registered and an unregistered egg should not be recorded in the tournament.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 392, in test_collision_between_registered_and_unregistered_egg_is_not_recorded
self.assertIs(registered_egg * unregistered_egg, registered_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6750d0>
======================================================================
FAIL: test_collision_between_two_registered_eggs_is_recorded (test.TestEggTournament.test_collision_between_two_registered_eggs_is_recorded)
A collision between two registered eggs should be recorded in the tournament.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 371, in test_collision_between_two_registered_eggs_is_recorded
self.assertIs(first_egg * second_egg, first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6747e0>
======================================================================
FAIL: test_collision_between_unregistered_eggs_is_not_recorded (test.TestEggTournament.test_collision_between_unregistered_eggs_is_not_recorded)
A collision between unregistered eggs should not be recorded in the tournament.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 381, in test_collision_between_unregistered_eggs_is_not_recorded
self.assertIs(first_egg * second_egg, first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f674c00>
======================================================================
FAIL: test_egg_broken_outside_tournament_remains_broken_inside_tournament (test.TestEggTournament.test_egg_broken_outside_tournament_remains_broken_inside_tournament)
Breaking an egg outside the tournament should preserve its broken state inside the tournament.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 404, in test_egg_broken_outside_tournament_remains_broken_inside_tournament
self.assertIs(broken_egg * outsider, outsider)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f675230>
======================================================================
FAIL: test_history_lookup_is_symmetric_with_respect_to_egg_order (test.TestEggTournament.test_history_lookup_is_symmetric_with_respect_to_egg_order)
Looking up a recorded collision in reversed egg order should return the same winner.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 445, in test_history_lookup_is_symmetric_with_respect_to_egg_order
self.assertIs(first_egg * second_egg, first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f675020>
======================================================================
FAIL: test_history_lookup_returns_winner_for_top_and_bottom (test.TestEggTournament.test_history_lookup_returns_winner_for_top_and_bottom)
Looking up recorded top and bottom collisions should return the correct winners.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 415, in test_history_lookup_returns_winner_for_top_and_bottom
self.assertIs(first_egg * second_egg, second_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f675910>
======================================================================
FAIL: test_history_lookup_top_and_bottom_are_distinct (test.TestEggTournament.test_history_lookup_top_and_bottom_are_distinct)
Looking up top and bottom collisions should treat them as distinct entries.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 456, in test_history_lookup_top_and_bottom_are_distinct
self.assertIs(first_egg * second_egg, second_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f675440>
======================================================================
FAIL: test_history_lookup_with_slice_key (test.TestEggTournament.test_history_lookup_with_slice_key)
Looking up a recorded collision with a slice key should return the winner.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 436, in test_history_lookup_with_slice_key
self.assertIs(first_egg * second_egg, first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6759c0>
======================================================================
FAIL: test_history_lookup_with_tuple_key (test.TestEggTournament.test_history_lookup_with_tuple_key)
Looking up a recorded collision with a tuple key should return the winner.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 427, in test_history_lookup_with_tuple_key
self.assertIs(first_egg * second_egg, first_egg)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f675ff0>
======================================================================
FAIL: test_ranking_missing_position_raises_index_error (test.TestEggTournament.test_ranking_missing_position_raises_index_error)
Looking up a missing ranking position should raise an IndexError.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 533, in test_ranking_missing_position_raises_index_error
self.assertIs(alpha * beta, alpha)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6760a0>
======================================================================
FAIL: test_ranking_tied_position_returns_set_of_eggs (test.TestEggTournament.test_ranking_tied_position_returns_set_of_eggs)
Looking up a tied ranking position should return a set of eggs.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 489, in test_ranking_tied_position_returns_set_of_eggs
self.assertIs(alpha * gamma, alpha)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f6764c0>
======================================================================
FAIL: test_ranking_unique_position_returns_single_egg (test.TestEggTournament.test_ranking_unique_position_returns_single_egg)
Looking up a unique ranking position should return a single egg.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 477, in test_ranking_unique_position_returns_single_egg
self.assertIs(alpha * beta, alpha)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f676a40>
======================================================================
FAIL: test_ranking_uses_dense_ranking_without_skipping_places (test.TestEggTournament.test_ranking_uses_dense_ranking_without_skipping_places)
Looking up ranking positions should use dense ranking without skipping places.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 509, in test_ranking_uses_dense_ranking_without_skipping_places
self.assertIs(alpha * beta, alpha)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f676af0>
======================================================================
FAIL: test_registered_egg_attribute_returns_position_and_victories (test.TestEggTournament.test_registered_egg_attribute_returns_position_and_victories)
Accessing a registered egg as an attribute should return its position and victories.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 556, in test_registered_egg_attribute_returns_position_and_victories
self.assertIs(alpha * beta, alpha)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f676d00>
======================================================================
FAIL: test_registered_egg_attribute_with_zero_victories_is_accessible (test.TestEggTournament.test_registered_egg_attribute_with_zero_victories_is_accessible)
Accessing a registered egg with zero victories should return its position and victories.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 572, in test_registered_egg_attribute_with_zero_victories_is_accessible
self.assertIs(alpha * beta, alpha)
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
AssertionError: None is not <solution.Egg object at 0x7bf66f677490>
----------------------------------------------------------------------
Ran 46 tests in 0.008s
FAILED (failures=32, errors=3)
Виктор Бечев
15.04.2026 16:02Като цяло местата, на които не си решил проблем с `if-elif-else` са супер. И употребата на такива условия не е сама по себе си грешна. Просто си тръгнал в някаква посока и в даден момент си стигнал до ситуация, в която си бил с вързани ръце и това е било логичното решение. Ако ти е любопитно - можеш да разгледаш други решения, за да видиш по-различен подход. Защото за обемен код - `if`-oвете стигат само до един момент.
|
Иван Грозданов
15.04.2026 15:31Fuuuck! Наистина сте го споменали в примерите просто не съм му обърнал внимание. Благодаря за точките все пак! 😁
|
Виктор Бечев
15.04.2026 14:41Важно е да е да връщаш победител при сблъсък на яйца, както в примерите. Ако го беше имплементирал - би имал доста повече точки. Получаваш бонус точка, защото все пак си се постарал. Просто си пропуснал ключова част от задачата, без която няма как да минат повечето тестове.
Вероятно би могъл _(резонно)_ да ни репликираш, че не сме упоменали изрично, че трябва нещо да бъде върнато. И ще си наполовина прав. Имаме това:
```# my_egg @ your_egg би върнало същото, но повече за това в следващия абзац```
Но трябва да признаем, че е малко скришно. Затова ще ти дадем още една от точките, които иначе би заслужил. Cheers.
|
| f | 1 | from collections import defaultdict | f | 1 | from collections import defaultdict |
| 2 | 2 | ||||
| 3 | class Egg: | 3 | class Egg: | ||
| 4 | __MAX_SPACE = 50.0 | 4 | __MAX_SPACE = 50.0 | ||
| 5 | __BASE_HARDNESS = 0.0 | 5 | __BASE_HARDNESS = 0.0 | ||
| 6 | 6 | ||||
| 7 | def __init__(self): | 7 | def __init__(self): | ||
| 8 | self.tournament = None | 8 | self.tournament = None | ||
| 9 | self.victories_in_tournament = 0 | 9 | self.victories_in_tournament = 0 | ||
| 10 | self.battles_history = [] | 10 | self.battles_history = [] | ||
| 11 | self.top_hardness = Egg.__BASE_HARDNESS | 11 | self.top_hardness = Egg.__BASE_HARDNESS | ||
| 12 | self.bottom_hardness = Egg.__BASE_HARDNESS | 12 | self.bottom_hardness = Egg.__BASE_HARDNESS | ||
| 13 | self.__colors = [] | 13 | self.__colors = [] | ||
| 14 | self.__top_not_painted_space = Egg.__MAX_SPACE | 14 | self.__top_not_painted_space = Egg.__MAX_SPACE | ||
| 15 | self.__bottom_not_painted_space = Egg.__MAX_SPACE | 15 | self.__bottom_not_painted_space = Egg.__MAX_SPACE | ||
| 16 | 16 | ||||
| 17 | def lost(self, which_half): | 17 | def lost(self, which_half): | ||
| 18 | setattr(self, which_half, None) | 18 | setattr(self, which_half, None) | ||
| 19 | 19 | ||||
| 20 | def __to_numeric_rgb(self, color): | 20 | def __to_numeric_rgb(self, color): | ||
| 21 | return tuple(int(color[i:i+2].upper(), 16) for i in (0, 2, 4)) | 21 | return tuple(int(color[i:i+2].upper(), 16) for i in (0, 2, 4)) | ||
| 22 | 22 | ||||
| 23 | def __give_hardness(self, which_half, *color): | 23 | def __give_hardness(self, which_half, *color): | ||
| 24 | if which_half == "top": | 24 | if which_half == "top": | ||
| 25 | self.top_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | 25 | self.top_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | ||
| 26 | elif which_half == "bottom": | 26 | elif which_half == "bottom": | ||
| 27 | self.bottom_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | 27 | self.bottom_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | ||
| 28 | 28 | ||||
| 29 | def paint(self, *colors): | 29 | def paint(self, *colors): | ||
| 30 | for color in colors: | 30 | for color in colors: | ||
| 31 | if self.__top_not_painted_space + self.__bottom_not_painted_space < color[1]: | 31 | if self.__top_not_painted_space + self.__bottom_not_painted_space < color[1]: | ||
| 32 | raise ValueError("Not enough space to paint the egg with the given color.") | 32 | raise ValueError("Not enough space to paint the egg with the given color.") | ||
| 33 | elif self.__top_not_painted_space >= color[1]: | 33 | elif self.__top_not_painted_space >= color[1]: | ||
| 34 | self.__give_hardness("top", color[0].upper(), color[1]) | 34 | self.__give_hardness("top", color[0].upper(), color[1]) | ||
| 35 | self.__top_not_painted_space -= color[1] | 35 | self.__top_not_painted_space -= color[1] | ||
| 36 | self.__colors.append(color) | 36 | self.__colors.append(color) | ||
| 37 | elif self.__top_not_painted_space < color[1]: | 37 | elif self.__top_not_painted_space < color[1]: | ||
| 38 | self.__give_hardness("top", color[0].upper(), self.__top_not_painted_space) | 38 | self.__give_hardness("top", color[0].upper(), self.__top_not_painted_space) | ||
| 39 | self.__give_hardness("bottom", color[0].upper(), color[1] - self.__top_not_painted_space) | 39 | self.__give_hardness("bottom", color[0].upper(), color[1] - self.__top_not_painted_space) | ||
| 40 | self.__top_not_painted_space = 0.0 | 40 | self.__top_not_painted_space = 0.0 | ||
| 41 | self.__bottom_not_painted_space -= (color[1] - self.__top_not_painted_space) | 41 | self.__bottom_not_painted_space -= (color[1] - self.__top_not_painted_space) | ||
| 42 | self.__colors.append(color) | 42 | self.__colors.append(color) | ||
| 43 | elif self.__top_not_painted_space == 0.0: | 43 | elif self.__top_not_painted_space == 0.0: | ||
| 44 | self.__give_hardness("bottom", color[0].upper(), color[1]) | 44 | self.__give_hardness("bottom", color[0].upper(), color[1]) | ||
| 45 | self.__bottom_not_painted_space -= color[1] | 45 | self.__bottom_not_painted_space -= color[1] | ||
| 46 | self.__colors.append(color) | 46 | self.__colors.append(color) | ||
| 47 | 47 | ||||
| 48 | def __add_battle(self, opponent, which_half, result): | 48 | def __add_battle(self, opponent, which_half, result): | ||
| 49 | self.battles_history.append((opponent, which_half, result)) | 49 | self.battles_history.append((opponent, which_half, result)) | ||
| 50 | if result == "win": | 50 | if result == "win": | ||
| 51 | self.victories_in_tournament += 1 | 51 | self.victories_in_tournament += 1 | ||
| 52 | 52 | ||||
| 53 | def return_battle_winner(self, opponent, which_half): | 53 | def return_battle_winner(self, opponent, which_half): | ||
| 54 | for battle in self.battles_history: | 54 | for battle in self.battles_history: | ||
| 55 | if battle[0] is opponent and battle[1] == which_half: | 55 | if battle[0] is opponent and battle[1] == which_half: | ||
| 56 | return self if battle[2] == "win" else opponent | 56 | return self if battle[2] == "win" else opponent | ||
| 57 | else: | 57 | else: | ||
| 58 | return None | 58 | return None | ||
| 59 | 59 | ||||
| 60 | def __mul__(self, other): | 60 | def __mul__(self, other): | ||
| 61 | if not isinstance(other, Egg): | 61 | if not isinstance(other, Egg): | ||
| 62 | raise ValueError("Can only multiply with another Egg instance.") | 62 | raise ValueError("Can only multiply with another Egg instance.") | ||
| 63 | 63 | ||||
| 64 | if self.top_hardness is None or other.top_hardness is None: | 64 | if self.top_hardness is None or other.top_hardness is None: | ||
| 65 | raise TypeError("Спукана работа :(") | 65 | raise TypeError("Спукана работа :(") | ||
| 66 | 66 | ||||
| 67 | if self.top_hardness > other.top_hardness: | 67 | if self.top_hardness > other.top_hardness: | ||
| 68 | if self.tournament is other.tournament and self.tournament: | 68 | if self.tournament is other.tournament and self.tournament: | ||
| 69 | self.__add_battle(other, "top", "win") | 69 | self.__add_battle(other, "top", "win") | ||
| 70 | other.__add_battle(self, "top", "lose") | 70 | other.__add_battle(self, "top", "lose") | ||
| 71 | other.lost("top_hardness") | 71 | other.lost("top_hardness") | ||
| 72 | elif self.top_hardness < other.top_hardness: | 72 | elif self.top_hardness < other.top_hardness: | ||
| 73 | if self.tournament is other.tournament and self.tournament: | 73 | if self.tournament is other.tournament and self.tournament: | ||
| 74 | self.__add_battle(other, "top", "lose") | 74 | self.__add_battle(other, "top", "lose") | ||
| 75 | other.__add_battle(self, "top", "win") | 75 | other.__add_battle(self, "top", "win") | ||
| 76 | self.lost("top_hardness") | 76 | self.lost("top_hardness") | ||
| 77 | elif self.top_hardness == 0.0: | 77 | elif self.top_hardness == 0.0: | ||
| 78 | if self.__top_not_painted_space < self.__MAX_SPACE and other.__top_not_painted_space == self.__MAX_SPACE: | 78 | if self.__top_not_painted_space < self.__MAX_SPACE and other.__top_not_painted_space == self.__MAX_SPACE: | ||
| 79 | if self.tournament is other.tournament and self.tournament: | 79 | if self.tournament is other.tournament and self.tournament: | ||
| 80 | self.__add_battle(other, "top", "win") | 80 | self.__add_battle(other, "top", "win") | ||
| 81 | other.__add_battle(self, "top", "lose") | 81 | other.__add_battle(self, "top", "lose") | ||
| 82 | other.lost("top_hardness") | 82 | other.lost("top_hardness") | ||
| 83 | elif self.__top_not_painted_space == self.__MAX_SPACE and other.__top_not_painted_space < self.__MAX_SPACE: | 83 | elif self.__top_not_painted_space == self.__MAX_SPACE and other.__top_not_painted_space < self.__MAX_SPACE: | ||
| 84 | if self.tournament is other.tournament and self.tournament: | 84 | if self.tournament is other.tournament and self.tournament: | ||
| 85 | self.__add_battle(other, "top", "lose") | 85 | self.__add_battle(other, "top", "lose") | ||
| 86 | other.__add_battle(self, "top", "win") | 86 | other.__add_battle(self, "top", "win") | ||
| 87 | self.lost("top_hardness") | 87 | self.lost("top_hardness") | ||
| 88 | 88 | ||||
| 89 | def __matmul__(self, other): | 89 | def __matmul__(self, other): | ||
| 90 | if not isinstance(other, Egg): | 90 | if not isinstance(other, Egg): | ||
| 91 | raise ValueError("Can only multiply with another Egg instance.") | 91 | raise ValueError("Can only multiply with another Egg instance.") | ||
| 92 | 92 | ||||
| 93 | if self.bottom_hardness is None or other.bottom_hardness is None: | 93 | if self.bottom_hardness is None or other.bottom_hardness is None: | ||
| 94 | raise TypeError("Спукана работа :(") | 94 | raise TypeError("Спукана работа :(") | ||
| 95 | 95 | ||||
| 96 | if self.bottom_hardness > other.bottom_hardness: | 96 | if self.bottom_hardness > other.bottom_hardness: | ||
| 97 | if self.tournament is other.tournament and self.tournament: | 97 | if self.tournament is other.tournament and self.tournament: | ||
| 98 | self.__add_battle(other, "bottom", "win") | 98 | self.__add_battle(other, "bottom", "win") | ||
| 99 | other.__add_battle(self, "bottom", "lose") | 99 | other.__add_battle(self, "bottom", "lose") | ||
| 100 | other.lost("bottom_hardness") | 100 | other.lost("bottom_hardness") | ||
| 101 | elif self.bottom_hardness < other.bottom_hardness: | 101 | elif self.bottom_hardness < other.bottom_hardness: | ||
| 102 | if self.tournament is other.tournament and self.tournament: | 102 | if self.tournament is other.tournament and self.tournament: | ||
| 103 | self.__add_battle(other, "bottom", "lose") | 103 | self.__add_battle(other, "bottom", "lose") | ||
| 104 | other.__add_battle(self, "bottom", "win") | 104 | other.__add_battle(self, "bottom", "win") | ||
| 105 | self.lost("bottom_hardness") | 105 | self.lost("bottom_hardness") | ||
| 106 | elif self.bottom_hardness == 0.0: | 106 | elif self.bottom_hardness == 0.0: | ||
| 107 | if self.__bottom_not_painted_space < self.__MAX_SPACE and other.__bottom_not_painted_space == self.__MAX_SPACE: | 107 | if self.__bottom_not_painted_space < self.__MAX_SPACE and other.__bottom_not_painted_space == self.__MAX_SPACE: | ||
| 108 | if self.tournament is other.tournament and self.tournament: | 108 | if self.tournament is other.tournament and self.tournament: | ||
| 109 | self.__add_battle(other, "bottom", "win") | 109 | self.__add_battle(other, "bottom", "win") | ||
| 110 | other.__add_battle(self, "bottom", "lose") | 110 | other.__add_battle(self, "bottom", "lose") | ||
| 111 | other.lost("bottom_hardness") | 111 | other.lost("bottom_hardness") | ||
| 112 | elif self.__bottom_not_painted_space == self.__MAX_SPACE and other.__bottom_not_painted_space < self.__MAX_SPACE: | 112 | elif self.__bottom_not_painted_space == self.__MAX_SPACE and other.__bottom_not_painted_space < self.__MAX_SPACE: | ||
| 113 | if self.tournament is other.tournament and self.tournament: | 113 | if self.tournament is other.tournament and self.tournament: | ||
| 114 | self.__add_battle(other, "bottom", "lose") | 114 | self.__add_battle(other, "bottom", "lose") | ||
| 115 | other.__add_battle(self, "bottom", "win") | 115 | other.__add_battle(self, "bottom", "win") | ||
| 116 | self.lost("bottom_hardness") | 116 | self.lost("bottom_hardness") | ||
| 117 | 117 | ||||
| 118 | 118 | ||||
| 119 | class EggTournament: | 119 | class EggTournament: | ||
| 120 | 120 | ||||
| 121 | def __init__(self): | 121 | def __init__(self): | ||
| 122 | self.__registered_eggs = {} | 122 | self.__registered_eggs = {} | ||
| 123 | 123 | ||||
| 124 | @property | 124 | @property | ||
| 125 | def __ranking(self): | 125 | def __ranking(self): | ||
| 126 | grouped_by_victories = defaultdict(list) | 126 | grouped_by_victories = defaultdict(list) | ||
| 127 | for name, egg in self.__registered_eggs.items(): | 127 | for name, egg in self.__registered_eggs.items(): | ||
| 128 | grouped_by_victories[egg.victories_in_tournament].append(name) | 128 | grouped_by_victories[egg.victories_in_tournament].append(name) | ||
| 129 | 129 | ||||
| 130 | sorted_victories = sorted(grouped_by_victories.keys(), reverse=True) | 130 | sorted_victories = sorted(grouped_by_victories.keys(), reverse=True) | ||
| 131 | result = {} | 131 | result = {} | ||
| 132 | position = 1 | 132 | position = 1 | ||
| 133 | for victories in sorted_victories: | 133 | for victories in sorted_victories: | ||
| 134 | result[position] = grouped_by_victories[victories] | 134 | result[position] = grouped_by_victories[victories] | ||
| 135 | position += 1 | 135 | position += 1 | ||
| 136 | return result | 136 | return result | ||
| 137 | 137 | ||||
| 138 | def register(self, egg, name): | 138 | def register(self, egg, name): | ||
| 139 | if any(name == reg_egg_name for reg_egg_name in self.__registered_eggs.keys()): | 139 | if any(name == reg_egg_name for reg_egg_name in self.__registered_eggs.keys()): | ||
| 140 | raise ValueError(f"Egg with name {name} has already been registered") | 140 | raise ValueError(f"Egg with name {name} has already been registered") | ||
| 141 | elif egg.tournament: | 141 | elif egg.tournament: | ||
| 142 | raise ValueError(f"An egg cannot be registered in multiple tournaments") | 142 | raise ValueError(f"An egg cannot be registered in multiple tournaments") | ||
| 143 | elif not name.isidentifier(): | 143 | elif not name.isidentifier(): | ||
| 144 | raise ValueError(f"Invalid registration name") | 144 | raise ValueError(f"Invalid registration name") | ||
| 145 | else: | 145 | else: | ||
| 146 | self.__registered_eggs[name] = egg | 146 | self.__registered_eggs[name] = egg | ||
| 147 | egg.tournament = self | 147 | egg.tournament = self | ||
| 148 | 148 | ||||
| 149 | def __getitem__(self, key): | 149 | def __getitem__(self, key): | ||
| 150 | if isinstance(key, slice): | 150 | if isinstance(key, slice): | ||
| 151 | egg1, egg2, which_half = key.start, key.stop, key.step | 151 | egg1, egg2, which_half = key.start, key.stop, key.step | ||
| 152 | else: | 152 | else: | ||
| 153 | egg1, egg2, which_half = key | 153 | egg1, egg2, which_half = key | ||
| 154 | if egg1 not in self.__registered_eggs.values() or egg2 not in self.__registered_eggs.values(): | 154 | if egg1 not in self.__registered_eggs.values() or egg2 not in self.__registered_eggs.values(): | ||
| 155 | raise KeyError("There is no such battle in the tournament") | 155 | raise KeyError("There is no such battle in the tournament") | ||
| 156 | if egg1.return_battle_winner(egg2, which_half) is None: | 156 | if egg1.return_battle_winner(egg2, which_half) is None: | ||
| 157 | raise KeyError("There is no such battle in the tournament") | 157 | raise KeyError("There is no such battle in the tournament") | ||
| 158 | return egg1.return_battle_winner(egg2, which_half) | 158 | return egg1.return_battle_winner(egg2, which_half) | ||
| 159 | 159 | ||||
| 160 | def __rmatmul__(self, position): | 160 | def __rmatmul__(self, position): | ||
| 161 | if position in self.__ranking.keys(): | 161 | if position in self.__ranking.keys(): | ||
| 162 | return {self.__registered_eggs[name] for name in self.__ranking[position]} | 162 | return {self.__registered_eggs[name] for name in self.__ranking[position]} | ||
| 163 | else: | 163 | else: | ||
| 164 | raise IndexError("There is no such position in the ranking table") | 164 | raise IndexError("There is no such position in the ranking table") | ||
| 165 | 165 | ||||
| 166 | def __getattr__(self, name): | 166 | def __getattr__(self, name): | ||
| 167 | for pos in self.__ranking.keys(): | 167 | for pos in self.__ranking.keys(): | ||
| 168 | if name in self.__ranking[pos]: | 168 | if name in self.__ranking[pos]: | ||
| 169 | rank = pos | 169 | rank = pos | ||
| 170 | break | 170 | break | ||
| n | n | 171 | else: | ||
| 172 | raise AttributeError("Apologies, there is no such egg registered") | ||||
| 171 | return {"position": rank, "victories": self.__registered_eggs[name].victories_in_tournament} if name in self.__registered_eggs else AttributeError(f"Apologies, there is no such egg registered") | 173 | return {"position": rank, "victories": self.__registered_eggs[name].victories_in_tournament} if name in self.__registered_eggs else AttributeError(f"Apologies, there is no such egg registered") | ||
| 172 | 174 | ||||
| 173 | def __contains__(self, egg): | 175 | def __contains__(self, egg): | ||
| t | 174 | if egg in self.__registered_eggs.values(): | t | 176 | return egg in self.__registered_eggs.values() |
| 175 | return True | ||||
| 176 | else: | ||||
| 177 | raise AttributeError("Apologies, there is no such egg registered") |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | from collections import defaultdict | f | 1 | from collections import defaultdict |
| 2 | 2 | ||||
| 3 | class Egg: | 3 | class Egg: | ||
| 4 | __MAX_SPACE = 50.0 | 4 | __MAX_SPACE = 50.0 | ||
| 5 | __BASE_HARDNESS = 0.0 | 5 | __BASE_HARDNESS = 0.0 | ||
| 6 | 6 | ||||
| 7 | def __init__(self): | 7 | def __init__(self): | ||
| 8 | self.tournament = None | 8 | self.tournament = None | ||
| 9 | self.victories_in_tournament = 0 | 9 | self.victories_in_tournament = 0 | ||
| 10 | self.battles_history = [] | 10 | self.battles_history = [] | ||
| 11 | self.top_hardness = Egg.__BASE_HARDNESS | 11 | self.top_hardness = Egg.__BASE_HARDNESS | ||
| 12 | self.bottom_hardness = Egg.__BASE_HARDNESS | 12 | self.bottom_hardness = Egg.__BASE_HARDNESS | ||
| 13 | self.__colors = [] | 13 | self.__colors = [] | ||
| 14 | self.__top_not_painted_space = Egg.__MAX_SPACE | 14 | self.__top_not_painted_space = Egg.__MAX_SPACE | ||
| 15 | self.__bottom_not_painted_space = Egg.__MAX_SPACE | 15 | self.__bottom_not_painted_space = Egg.__MAX_SPACE | ||
| 16 | 16 | ||||
| 17 | def lost(self, which_half): | 17 | def lost(self, which_half): | ||
| 18 | setattr(self, which_half, None) | 18 | setattr(self, which_half, None) | ||
| 19 | 19 | ||||
| 20 | def __to_numeric_rgb(self, color): | 20 | def __to_numeric_rgb(self, color): | ||
| 21 | return tuple(int(color[i:i+2].upper(), 16) for i in (0, 2, 4)) | 21 | return tuple(int(color[i:i+2].upper(), 16) for i in (0, 2, 4)) | ||
| 22 | 22 | ||||
| 23 | def __give_hardness(self, which_half, *color): | 23 | def __give_hardness(self, which_half, *color): | ||
| 24 | if which_half == "top": | 24 | if which_half == "top": | ||
| 25 | self.top_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | 25 | self.top_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | ||
| 26 | elif which_half == "bottom": | 26 | elif which_half == "bottom": | ||
| 27 | self.bottom_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | 27 | self.bottom_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | ||
| 28 | 28 | ||||
| 29 | def paint(self, *colors): | 29 | def paint(self, *colors): | ||
| 30 | for color in colors: | 30 | for color in colors: | ||
| 31 | if self.__top_not_painted_space + self.__bottom_not_painted_space < color[1]: | 31 | if self.__top_not_painted_space + self.__bottom_not_painted_space < color[1]: | ||
| 32 | raise ValueError("Not enough space to paint the egg with the given color.") | 32 | raise ValueError("Not enough space to paint the egg with the given color.") | ||
| 33 | elif self.__top_not_painted_space >= color[1]: | 33 | elif self.__top_not_painted_space >= color[1]: | ||
| 34 | self.__give_hardness("top", color[0].upper(), color[1]) | 34 | self.__give_hardness("top", color[0].upper(), color[1]) | ||
| 35 | self.__top_not_painted_space -= color[1] | 35 | self.__top_not_painted_space -= color[1] | ||
| 36 | self.__colors.append(color) | 36 | self.__colors.append(color) | ||
| 37 | elif self.__top_not_painted_space < color[1]: | 37 | elif self.__top_not_painted_space < color[1]: | ||
| 38 | self.__give_hardness("top", color[0].upper(), self.__top_not_painted_space) | 38 | self.__give_hardness("top", color[0].upper(), self.__top_not_painted_space) | ||
| 39 | self.__give_hardness("bottom", color[0].upper(), color[1] - self.__top_not_painted_space) | 39 | self.__give_hardness("bottom", color[0].upper(), color[1] - self.__top_not_painted_space) | ||
| 40 | self.__top_not_painted_space = 0.0 | 40 | self.__top_not_painted_space = 0.0 | ||
| 41 | self.__bottom_not_painted_space -= (color[1] - self.__top_not_painted_space) | 41 | self.__bottom_not_painted_space -= (color[1] - self.__top_not_painted_space) | ||
| 42 | self.__colors.append(color) | 42 | self.__colors.append(color) | ||
| 43 | elif self.__top_not_painted_space == 0.0: | 43 | elif self.__top_not_painted_space == 0.0: | ||
| 44 | self.__give_hardness("bottom", color[0].upper(), color[1]) | 44 | self.__give_hardness("bottom", color[0].upper(), color[1]) | ||
| 45 | self.__bottom_not_painted_space -= color[1] | 45 | self.__bottom_not_painted_space -= color[1] | ||
| 46 | self.__colors.append(color) | 46 | self.__colors.append(color) | ||
| 47 | 47 | ||||
| 48 | def __add_battle(self, opponent, which_half, result): | 48 | def __add_battle(self, opponent, which_half, result): | ||
| 49 | self.battles_history.append((opponent, which_half, result)) | 49 | self.battles_history.append((opponent, which_half, result)) | ||
| 50 | if result == "win": | 50 | if result == "win": | ||
| 51 | self.victories_in_tournament += 1 | 51 | self.victories_in_tournament += 1 | ||
| 52 | 52 | ||||
| 53 | def return_battle_winner(self, opponent, which_half): | 53 | def return_battle_winner(self, opponent, which_half): | ||
| 54 | for battle in self.battles_history: | 54 | for battle in self.battles_history: | ||
| 55 | if battle[0] is opponent and battle[1] == which_half: | 55 | if battle[0] is opponent and battle[1] == which_half: | ||
| 56 | return self if battle[2] == "win" else opponent | 56 | return self if battle[2] == "win" else opponent | ||
| 57 | else: | 57 | else: | ||
| 58 | return None | 58 | return None | ||
| 59 | 59 | ||||
| 60 | def __mul__(self, other): | 60 | def __mul__(self, other): | ||
| 61 | if not isinstance(other, Egg): | 61 | if not isinstance(other, Egg): | ||
| 62 | raise ValueError("Can only multiply with another Egg instance.") | 62 | raise ValueError("Can only multiply with another Egg instance.") | ||
| 63 | 63 | ||||
| 64 | if self.top_hardness is None or other.top_hardness is None: | 64 | if self.top_hardness is None or other.top_hardness is None: | ||
| 65 | raise TypeError("Спукана работа :(") | 65 | raise TypeError("Спукана работа :(") | ||
| 66 | 66 | ||||
| 67 | if self.top_hardness > other.top_hardness: | 67 | if self.top_hardness > other.top_hardness: | ||
| 68 | if self.tournament is other.tournament and self.tournament: | 68 | if self.tournament is other.tournament and self.tournament: | ||
| 69 | self.__add_battle(other, "top", "win") | 69 | self.__add_battle(other, "top", "win") | ||
| 70 | other.__add_battle(self, "top", "lose") | 70 | other.__add_battle(self, "top", "lose") | ||
| 71 | other.lost("top_hardness") | 71 | other.lost("top_hardness") | ||
| 72 | elif self.top_hardness < other.top_hardness: | 72 | elif self.top_hardness < other.top_hardness: | ||
| 73 | if self.tournament is other.tournament and self.tournament: | 73 | if self.tournament is other.tournament and self.tournament: | ||
| 74 | self.__add_battle(other, "top", "lose") | 74 | self.__add_battle(other, "top", "lose") | ||
| 75 | other.__add_battle(self, "top", "win") | 75 | other.__add_battle(self, "top", "win") | ||
| 76 | self.lost("top_hardness") | 76 | self.lost("top_hardness") | ||
| 77 | elif self.top_hardness == 0.0: | 77 | elif self.top_hardness == 0.0: | ||
| 78 | if self.__top_not_painted_space < self.__MAX_SPACE and other.__top_not_painted_space == self.__MAX_SPACE: | 78 | if self.__top_not_painted_space < self.__MAX_SPACE and other.__top_not_painted_space == self.__MAX_SPACE: | ||
| 79 | if self.tournament is other.tournament and self.tournament: | 79 | if self.tournament is other.tournament and self.tournament: | ||
| 80 | self.__add_battle(other, "top", "win") | 80 | self.__add_battle(other, "top", "win") | ||
| 81 | other.__add_battle(self, "top", "lose") | 81 | other.__add_battle(self, "top", "lose") | ||
| 82 | other.lost("top_hardness") | 82 | other.lost("top_hardness") | ||
| 83 | elif self.__top_not_painted_space == self.__MAX_SPACE and other.__top_not_painted_space < self.__MAX_SPACE: | 83 | elif self.__top_not_painted_space == self.__MAX_SPACE and other.__top_not_painted_space < self.__MAX_SPACE: | ||
| 84 | if self.tournament is other.tournament and self.tournament: | 84 | if self.tournament is other.tournament and self.tournament: | ||
| 85 | self.__add_battle(other, "top", "lose") | 85 | self.__add_battle(other, "top", "lose") | ||
| 86 | other.__add_battle(self, "top", "win") | 86 | other.__add_battle(self, "top", "win") | ||
| 87 | self.lost("top_hardness") | 87 | self.lost("top_hardness") | ||
| 88 | 88 | ||||
| 89 | def __matmul__(self, other): | 89 | def __matmul__(self, other): | ||
| 90 | if not isinstance(other, Egg): | 90 | if not isinstance(other, Egg): | ||
| 91 | raise ValueError("Can only multiply with another Egg instance.") | 91 | raise ValueError("Can only multiply with another Egg instance.") | ||
| 92 | 92 | ||||
| 93 | if self.bottom_hardness is None or other.bottom_hardness is None: | 93 | if self.bottom_hardness is None or other.bottom_hardness is None: | ||
| 94 | raise TypeError("Спукана работа :(") | 94 | raise TypeError("Спукана работа :(") | ||
| 95 | 95 | ||||
| 96 | if self.bottom_hardness > other.bottom_hardness: | 96 | if self.bottom_hardness > other.bottom_hardness: | ||
| 97 | if self.tournament is other.tournament and self.tournament: | 97 | if self.tournament is other.tournament and self.tournament: | ||
| 98 | self.__add_battle(other, "bottom", "win") | 98 | self.__add_battle(other, "bottom", "win") | ||
| 99 | other.__add_battle(self, "bottom", "lose") | 99 | other.__add_battle(self, "bottom", "lose") | ||
| 100 | other.lost("bottom_hardness") | 100 | other.lost("bottom_hardness") | ||
| 101 | elif self.bottom_hardness < other.bottom_hardness: | 101 | elif self.bottom_hardness < other.bottom_hardness: | ||
| 102 | if self.tournament is other.tournament and self.tournament: | 102 | if self.tournament is other.tournament and self.tournament: | ||
| 103 | self.__add_battle(other, "bottom", "lose") | 103 | self.__add_battle(other, "bottom", "lose") | ||
| 104 | other.__add_battle(self, "bottom", "win") | 104 | other.__add_battle(self, "bottom", "win") | ||
| 105 | self.lost("bottom_hardness") | 105 | self.lost("bottom_hardness") | ||
| 106 | elif self.bottom_hardness == 0.0: | 106 | elif self.bottom_hardness == 0.0: | ||
| 107 | if self.__bottom_not_painted_space < self.__MAX_SPACE and other.__bottom_not_painted_space == self.__MAX_SPACE: | 107 | if self.__bottom_not_painted_space < self.__MAX_SPACE and other.__bottom_not_painted_space == self.__MAX_SPACE: | ||
| 108 | if self.tournament is other.tournament and self.tournament: | 108 | if self.tournament is other.tournament and self.tournament: | ||
| 109 | self.__add_battle(other, "bottom", "win") | 109 | self.__add_battle(other, "bottom", "win") | ||
| 110 | other.__add_battle(self, "bottom", "lose") | 110 | other.__add_battle(self, "bottom", "lose") | ||
| 111 | other.lost("bottom_hardness") | 111 | other.lost("bottom_hardness") | ||
| 112 | elif self.__bottom_not_painted_space == self.__MAX_SPACE and other.__bottom_not_painted_space < self.__MAX_SPACE: | 112 | elif self.__bottom_not_painted_space == self.__MAX_SPACE and other.__bottom_not_painted_space < self.__MAX_SPACE: | ||
| 113 | if self.tournament is other.tournament and self.tournament: | 113 | if self.tournament is other.tournament and self.tournament: | ||
| 114 | self.__add_battle(other, "bottom", "lose") | 114 | self.__add_battle(other, "bottom", "lose") | ||
| 115 | other.__add_battle(self, "bottom", "win") | 115 | other.__add_battle(self, "bottom", "win") | ||
| 116 | self.lost("bottom_hardness") | 116 | self.lost("bottom_hardness") | ||
| 117 | 117 | ||||
| 118 | 118 | ||||
| 119 | class EggTournament: | 119 | class EggTournament: | ||
| 120 | 120 | ||||
| 121 | def __init__(self): | 121 | def __init__(self): | ||
| 122 | self.__registered_eggs = {} | 122 | self.__registered_eggs = {} | ||
| 123 | 123 | ||||
| 124 | @property | 124 | @property | ||
| 125 | def __ranking(self): | 125 | def __ranking(self): | ||
| 126 | grouped_by_victories = defaultdict(list) | 126 | grouped_by_victories = defaultdict(list) | ||
| 127 | for name, egg in self.__registered_eggs.items(): | 127 | for name, egg in self.__registered_eggs.items(): | ||
| 128 | grouped_by_victories[egg.victories_in_tournament].append(name) | 128 | grouped_by_victories[egg.victories_in_tournament].append(name) | ||
| 129 | 129 | ||||
| 130 | sorted_victories = sorted(grouped_by_victories.keys(), reverse=True) | 130 | sorted_victories = sorted(grouped_by_victories.keys(), reverse=True) | ||
| 131 | result = {} | 131 | result = {} | ||
| 132 | position = 1 | 132 | position = 1 | ||
| 133 | for victories in sorted_victories: | 133 | for victories in sorted_victories: | ||
| 134 | result[position] = grouped_by_victories[victories] | 134 | result[position] = grouped_by_victories[victories] | ||
| 135 | position += 1 | 135 | position += 1 | ||
| 136 | return result | 136 | return result | ||
| 137 | 137 | ||||
| 138 | def register(self, egg, name): | 138 | def register(self, egg, name): | ||
| 139 | if any(name == reg_egg_name for reg_egg_name in self.__registered_eggs.keys()): | 139 | if any(name == reg_egg_name for reg_egg_name in self.__registered_eggs.keys()): | ||
| 140 | raise ValueError(f"Egg with name {name} has already been registered") | 140 | raise ValueError(f"Egg with name {name} has already been registered") | ||
| 141 | elif egg.tournament: | 141 | elif egg.tournament: | ||
| 142 | raise ValueError(f"An egg cannot be registered in multiple tournaments") | 142 | raise ValueError(f"An egg cannot be registered in multiple tournaments") | ||
| 143 | elif not name.isidentifier(): | 143 | elif not name.isidentifier(): | ||
| 144 | raise ValueError(f"Invalid registration name") | 144 | raise ValueError(f"Invalid registration name") | ||
| 145 | else: | 145 | else: | ||
| 146 | self.__registered_eggs[name] = egg | 146 | self.__registered_eggs[name] = egg | ||
| 147 | egg.tournament = self | 147 | egg.tournament = self | ||
| 148 | 148 | ||||
| 149 | def __getitem__(self, key): | 149 | def __getitem__(self, key): | ||
| 150 | if isinstance(key, slice): | 150 | if isinstance(key, slice): | ||
| 151 | egg1, egg2, which_half = key.start, key.stop, key.step | 151 | egg1, egg2, which_half = key.start, key.stop, key.step | ||
| 152 | else: | 152 | else: | ||
| 153 | egg1, egg2, which_half = key | 153 | egg1, egg2, which_half = key | ||
| 154 | if egg1 not in self.__registered_eggs.values() or egg2 not in self.__registered_eggs.values(): | 154 | if egg1 not in self.__registered_eggs.values() or egg2 not in self.__registered_eggs.values(): | ||
| 155 | raise KeyError("There is no such battle in the tournament") | 155 | raise KeyError("There is no such battle in the tournament") | ||
| 156 | if egg1.return_battle_winner(egg2, which_half) is None: | 156 | if egg1.return_battle_winner(egg2, which_half) is None: | ||
| 157 | raise KeyError("There is no such battle in the tournament") | 157 | raise KeyError("There is no such battle in the tournament") | ||
| 158 | return egg1.return_battle_winner(egg2, which_half) | 158 | return egg1.return_battle_winner(egg2, which_half) | ||
| 159 | 159 | ||||
| 160 | def __rmatmul__(self, position): | 160 | def __rmatmul__(self, position): | ||
| 161 | if position in self.__ranking.keys(): | 161 | if position in self.__ranking.keys(): | ||
| 162 | return {self.__registered_eggs[name] for name in self.__ranking[position]} | 162 | return {self.__registered_eggs[name] for name in self.__ranking[position]} | ||
| 163 | else: | 163 | else: | ||
| 164 | raise IndexError("There is no such position in the ranking table") | 164 | raise IndexError("There is no such position in the ranking table") | ||
| 165 | 165 | ||||
| 166 | def __getattr__(self, name): | 166 | def __getattr__(self, name): | ||
| 167 | for pos in self.__ranking.keys(): | 167 | for pos in self.__ranking.keys(): | ||
| 168 | if name in self.__ranking[pos]: | 168 | if name in self.__ranking[pos]: | ||
| 169 | rank = pos | 169 | rank = pos | ||
| 170 | break | 170 | break | ||
| 171 | return {"position": rank, "victories": self.__registered_eggs[name].victories_in_tournament} if name in self.__registered_eggs else AttributeError(f"Apologies, there is no such egg registered") | 171 | return {"position": rank, "victories": self.__registered_eggs[name].victories_in_tournament} if name in self.__registered_eggs else AttributeError(f"Apologies, there is no such egg registered") | ||
| 172 | 172 | ||||
| 173 | def __contains__(self, egg): | 173 | def __contains__(self, egg): | ||
| t | 174 | return egg in self.__registered_eggs.values() | t | 174 | if egg in self.__registered_eggs.values(): |
| 175 | return True | ||||
| 176 | else: | ||||
| 177 | raise AttributeError("Apologies, there is no such egg registered") |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | from collections import defaultdict | f | 1 | from collections import defaultdict |
| 2 | 2 | ||||
| 3 | class Egg: | 3 | class Egg: | ||
| 4 | __MAX_SPACE = 50.0 | 4 | __MAX_SPACE = 50.0 | ||
| 5 | __BASE_HARDNESS = 0.0 | 5 | __BASE_HARDNESS = 0.0 | ||
| 6 | 6 | ||||
| 7 | def __init__(self): | 7 | def __init__(self): | ||
| 8 | self.tournament = None | 8 | self.tournament = None | ||
| 9 | self.victories_in_tournament = 0 | 9 | self.victories_in_tournament = 0 | ||
| 10 | self.battles_history = [] | 10 | self.battles_history = [] | ||
| 11 | self.top_hardness = Egg.__BASE_HARDNESS | 11 | self.top_hardness = Egg.__BASE_HARDNESS | ||
| 12 | self.bottom_hardness = Egg.__BASE_HARDNESS | 12 | self.bottom_hardness = Egg.__BASE_HARDNESS | ||
| 13 | self.__colors = [] | 13 | self.__colors = [] | ||
| 14 | self.__top_not_painted_space = Egg.__MAX_SPACE | 14 | self.__top_not_painted_space = Egg.__MAX_SPACE | ||
| 15 | self.__bottom_not_painted_space = Egg.__MAX_SPACE | 15 | self.__bottom_not_painted_space = Egg.__MAX_SPACE | ||
| 16 | 16 | ||||
| 17 | def lost(self, which_half): | 17 | def lost(self, which_half): | ||
| 18 | setattr(self, which_half, None) | 18 | setattr(self, which_half, None) | ||
| 19 | 19 | ||||
| 20 | def __to_numeric_rgb(self, color): | 20 | def __to_numeric_rgb(self, color): | ||
| 21 | return tuple(int(color[i:i+2].upper(), 16) for i in (0, 2, 4)) | 21 | return tuple(int(color[i:i+2].upper(), 16) for i in (0, 2, 4)) | ||
| 22 | 22 | ||||
| 23 | def __give_hardness(self, which_half, *color): | 23 | def __give_hardness(self, which_half, *color): | ||
| 24 | if which_half == "top": | 24 | if which_half == "top": | ||
| 25 | self.top_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | 25 | self.top_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | ||
| 26 | elif which_half == "bottom": | 26 | elif which_half == "bottom": | ||
| 27 | self.bottom_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | 27 | self.bottom_hardness += (color[1] / self.__MAX_SPACE) * sum(self.__to_numeric_rgb(color[0].upper())) | ||
| 28 | 28 | ||||
| 29 | def paint(self, *colors): | 29 | def paint(self, *colors): | ||
| 30 | for color in colors: | 30 | for color in colors: | ||
| 31 | if self.__top_not_painted_space + self.__bottom_not_painted_space < color[1]: | 31 | if self.__top_not_painted_space + self.__bottom_not_painted_space < color[1]: | ||
| 32 | raise ValueError("Not enough space to paint the egg with the given color.") | 32 | raise ValueError("Not enough space to paint the egg with the given color.") | ||
| 33 | elif self.__top_not_painted_space >= color[1]: | 33 | elif self.__top_not_painted_space >= color[1]: | ||
| 34 | self.__give_hardness("top", color[0].upper(), color[1]) | 34 | self.__give_hardness("top", color[0].upper(), color[1]) | ||
| 35 | self.__top_not_painted_space -= color[1] | 35 | self.__top_not_painted_space -= color[1] | ||
| 36 | self.__colors.append(color) | 36 | self.__colors.append(color) | ||
| 37 | elif self.__top_not_painted_space < color[1]: | 37 | elif self.__top_not_painted_space < color[1]: | ||
| 38 | self.__give_hardness("top", color[0].upper(), self.__top_not_painted_space) | 38 | self.__give_hardness("top", color[0].upper(), self.__top_not_painted_space) | ||
| 39 | self.__give_hardness("bottom", color[0].upper(), color[1] - self.__top_not_painted_space) | 39 | self.__give_hardness("bottom", color[0].upper(), color[1] - self.__top_not_painted_space) | ||
| 40 | self.__top_not_painted_space = 0.0 | 40 | self.__top_not_painted_space = 0.0 | ||
| 41 | self.__bottom_not_painted_space -= (color[1] - self.__top_not_painted_space) | 41 | self.__bottom_not_painted_space -= (color[1] - self.__top_not_painted_space) | ||
| 42 | self.__colors.append(color) | 42 | self.__colors.append(color) | ||
| 43 | elif self.__top_not_painted_space == 0.0: | 43 | elif self.__top_not_painted_space == 0.0: | ||
| 44 | self.__give_hardness("bottom", color[0].upper(), color[1]) | 44 | self.__give_hardness("bottom", color[0].upper(), color[1]) | ||
| 45 | self.__bottom_not_painted_space -= color[1] | 45 | self.__bottom_not_painted_space -= color[1] | ||
| 46 | self.__colors.append(color) | 46 | self.__colors.append(color) | ||
| 47 | 47 | ||||
| 48 | def __add_battle(self, opponent, which_half, result): | 48 | def __add_battle(self, opponent, which_half, result): | ||
| 49 | self.battles_history.append((opponent, which_half, result)) | 49 | self.battles_history.append((opponent, which_half, result)) | ||
| 50 | if result == "win": | 50 | if result == "win": | ||
| 51 | self.victories_in_tournament += 1 | 51 | self.victories_in_tournament += 1 | ||
| 52 | 52 | ||||
| 53 | def return_battle_winner(self, opponent, which_half): | 53 | def return_battle_winner(self, opponent, which_half): | ||
| 54 | for battle in self.battles_history: | 54 | for battle in self.battles_history: | ||
| 55 | if battle[0] is opponent and battle[1] == which_half: | 55 | if battle[0] is opponent and battle[1] == which_half: | ||
| 56 | return self if battle[2] == "win" else opponent | 56 | return self if battle[2] == "win" else opponent | ||
| 57 | else: | 57 | else: | ||
| 58 | return None | 58 | return None | ||
| 59 | 59 | ||||
| 60 | def __mul__(self, other): | 60 | def __mul__(self, other): | ||
| 61 | if not isinstance(other, Egg): | 61 | if not isinstance(other, Egg): | ||
| 62 | raise ValueError("Can only multiply with another Egg instance.") | 62 | raise ValueError("Can only multiply with another Egg instance.") | ||
| 63 | 63 | ||||
| 64 | if self.top_hardness is None or other.top_hardness is None: | 64 | if self.top_hardness is None or other.top_hardness is None: | ||
| 65 | raise TypeError("Спукана работа :(") | 65 | raise TypeError("Спукана работа :(") | ||
| 66 | 66 | ||||
| 67 | if self.top_hardness > other.top_hardness: | 67 | if self.top_hardness > other.top_hardness: | ||
| 68 | if self.tournament is other.tournament and self.tournament: | 68 | if self.tournament is other.tournament and self.tournament: | ||
| 69 | self.__add_battle(other, "top", "win") | 69 | self.__add_battle(other, "top", "win") | ||
| 70 | other.__add_battle(self, "top", "lose") | 70 | other.__add_battle(self, "top", "lose") | ||
| 71 | other.lost("top_hardness") | 71 | other.lost("top_hardness") | ||
| 72 | elif self.top_hardness < other.top_hardness: | 72 | elif self.top_hardness < other.top_hardness: | ||
| 73 | if self.tournament is other.tournament and self.tournament: | 73 | if self.tournament is other.tournament and self.tournament: | ||
| 74 | self.__add_battle(other, "top", "lose") | 74 | self.__add_battle(other, "top", "lose") | ||
| 75 | other.__add_battle(self, "top", "win") | 75 | other.__add_battle(self, "top", "win") | ||
| 76 | self.lost("top_hardness") | 76 | self.lost("top_hardness") | ||
| 77 | elif self.top_hardness == 0.0: | 77 | elif self.top_hardness == 0.0: | ||
| 78 | if self.__top_not_painted_space < self.__MAX_SPACE and other.__top_not_painted_space == self.__MAX_SPACE: | 78 | if self.__top_not_painted_space < self.__MAX_SPACE and other.__top_not_painted_space == self.__MAX_SPACE: | ||
| 79 | if self.tournament is other.tournament and self.tournament: | 79 | if self.tournament is other.tournament and self.tournament: | ||
| 80 | self.__add_battle(other, "top", "win") | 80 | self.__add_battle(other, "top", "win") | ||
| 81 | other.__add_battle(self, "top", "lose") | 81 | other.__add_battle(self, "top", "lose") | ||
| 82 | other.lost("top_hardness") | 82 | other.lost("top_hardness") | ||
| 83 | elif self.__top_not_painted_space == self.__MAX_SPACE and other.__top_not_painted_space < self.__MAX_SPACE: | 83 | elif self.__top_not_painted_space == self.__MAX_SPACE and other.__top_not_painted_space < self.__MAX_SPACE: | ||
| 84 | if self.tournament is other.tournament and self.tournament: | 84 | if self.tournament is other.tournament and self.tournament: | ||
| 85 | self.__add_battle(other, "top", "lose") | 85 | self.__add_battle(other, "top", "lose") | ||
| 86 | other.__add_battle(self, "top", "win") | 86 | other.__add_battle(self, "top", "win") | ||
| 87 | self.lost("top_hardness") | 87 | self.lost("top_hardness") | ||
| 88 | 88 | ||||
| 89 | def __matmul__(self, other): | 89 | def __matmul__(self, other): | ||
| 90 | if not isinstance(other, Egg): | 90 | if not isinstance(other, Egg): | ||
| 91 | raise ValueError("Can only multiply with another Egg instance.") | 91 | raise ValueError("Can only multiply with another Egg instance.") | ||
| 92 | 92 | ||||
| 93 | if self.bottom_hardness is None or other.bottom_hardness is None: | 93 | if self.bottom_hardness is None or other.bottom_hardness is None: | ||
| 94 | raise TypeError("Спукана работа :(") | 94 | raise TypeError("Спукана работа :(") | ||
| 95 | 95 | ||||
| 96 | if self.bottom_hardness > other.bottom_hardness: | 96 | if self.bottom_hardness > other.bottom_hardness: | ||
| 97 | if self.tournament is other.tournament and self.tournament: | 97 | if self.tournament is other.tournament and self.tournament: | ||
| 98 | self.__add_battle(other, "bottom", "win") | 98 | self.__add_battle(other, "bottom", "win") | ||
| 99 | other.__add_battle(self, "bottom", "lose") | 99 | other.__add_battle(self, "bottom", "lose") | ||
| 100 | other.lost("bottom_hardness") | 100 | other.lost("bottom_hardness") | ||
| 101 | elif self.bottom_hardness < other.bottom_hardness: | 101 | elif self.bottom_hardness < other.bottom_hardness: | ||
| 102 | if self.tournament is other.tournament and self.tournament: | 102 | if self.tournament is other.tournament and self.tournament: | ||
| 103 | self.__add_battle(other, "bottom", "lose") | 103 | self.__add_battle(other, "bottom", "lose") | ||
| 104 | other.__add_battle(self, "bottom", "win") | 104 | other.__add_battle(self, "bottom", "win") | ||
| 105 | self.lost("bottom_hardness") | 105 | self.lost("bottom_hardness") | ||
| 106 | elif self.bottom_hardness == 0.0: | 106 | elif self.bottom_hardness == 0.0: | ||
| 107 | if self.__bottom_not_painted_space < self.__MAX_SPACE and other.__bottom_not_painted_space == self.__MAX_SPACE: | 107 | if self.__bottom_not_painted_space < self.__MAX_SPACE and other.__bottom_not_painted_space == self.__MAX_SPACE: | ||
| 108 | if self.tournament is other.tournament and self.tournament: | 108 | if self.tournament is other.tournament and self.tournament: | ||
| 109 | self.__add_battle(other, "bottom", "win") | 109 | self.__add_battle(other, "bottom", "win") | ||
| 110 | other.__add_battle(self, "bottom", "lose") | 110 | other.__add_battle(self, "bottom", "lose") | ||
| 111 | other.lost("bottom_hardness") | 111 | other.lost("bottom_hardness") | ||
| 112 | elif self.__bottom_not_painted_space == self.__MAX_SPACE and other.__bottom_not_painted_space < self.__MAX_SPACE: | 112 | elif self.__bottom_not_painted_space == self.__MAX_SPACE and other.__bottom_not_painted_space < self.__MAX_SPACE: | ||
| 113 | if self.tournament is other.tournament and self.tournament: | 113 | if self.tournament is other.tournament and self.tournament: | ||
| 114 | self.__add_battle(other, "bottom", "lose") | 114 | self.__add_battle(other, "bottom", "lose") | ||
| 115 | other.__add_battle(self, "bottom", "win") | 115 | other.__add_battle(self, "bottom", "win") | ||
| 116 | self.lost("bottom_hardness") | 116 | self.lost("bottom_hardness") | ||
| 117 | 117 | ||||
| t | 118 | t | |||
| 119 | |||||
| 120 | 118 | ||||
| 121 | class EggTournament: | 119 | class EggTournament: | ||
| 122 | 120 | ||||
| 123 | def __init__(self): | 121 | def __init__(self): | ||
| 124 | self.__registered_eggs = {} | 122 | self.__registered_eggs = {} | ||
| 125 | 123 | ||||
| 126 | @property | 124 | @property | ||
| 127 | def __ranking(self): | 125 | def __ranking(self): | ||
| 128 | grouped_by_victories = defaultdict(list) | 126 | grouped_by_victories = defaultdict(list) | ||
| 129 | for name, egg in self.__registered_eggs.items(): | 127 | for name, egg in self.__registered_eggs.items(): | ||
| 130 | grouped_by_victories[egg.victories_in_tournament].append(name) | 128 | grouped_by_victories[egg.victories_in_tournament].append(name) | ||
| 131 | 129 | ||||
| 132 | sorted_victories = sorted(grouped_by_victories.keys(), reverse=True) | 130 | sorted_victories = sorted(grouped_by_victories.keys(), reverse=True) | ||
| 133 | result = {} | 131 | result = {} | ||
| 134 | position = 1 | 132 | position = 1 | ||
| 135 | for victories in sorted_victories: | 133 | for victories in sorted_victories: | ||
| 136 | result[position] = grouped_by_victories[victories] | 134 | result[position] = grouped_by_victories[victories] | ||
| 137 | position += 1 | 135 | position += 1 | ||
| 138 | return result | 136 | return result | ||
| 139 | 137 | ||||
| 140 | def register(self, egg, name): | 138 | def register(self, egg, name): | ||
| 141 | if any(name == reg_egg_name for reg_egg_name in self.__registered_eggs.keys()): | 139 | if any(name == reg_egg_name for reg_egg_name in self.__registered_eggs.keys()): | ||
| 142 | raise ValueError(f"Egg with name {name} has already been registered") | 140 | raise ValueError(f"Egg with name {name} has already been registered") | ||
| 143 | elif egg.tournament: | 141 | elif egg.tournament: | ||
| 144 | raise ValueError(f"An egg cannot be registered in multiple tournaments") | 142 | raise ValueError(f"An egg cannot be registered in multiple tournaments") | ||
| 145 | elif not name.isidentifier(): | 143 | elif not name.isidentifier(): | ||
| 146 | raise ValueError(f"Invalid registration name") | 144 | raise ValueError(f"Invalid registration name") | ||
| 147 | else: | 145 | else: | ||
| 148 | self.__registered_eggs[name] = egg | 146 | self.__registered_eggs[name] = egg | ||
| 149 | egg.tournament = self | 147 | egg.tournament = self | ||
| 150 | 148 | ||||
| 151 | def __getitem__(self, key): | 149 | def __getitem__(self, key): | ||
| 152 | if isinstance(key, slice): | 150 | if isinstance(key, slice): | ||
| 153 | egg1, egg2, which_half = key.start, key.stop, key.step | 151 | egg1, egg2, which_half = key.start, key.stop, key.step | ||
| 154 | else: | 152 | else: | ||
| 155 | egg1, egg2, which_half = key | 153 | egg1, egg2, which_half = key | ||
| 156 | if egg1 not in self.__registered_eggs.values() or egg2 not in self.__registered_eggs.values(): | 154 | if egg1 not in self.__registered_eggs.values() or egg2 not in self.__registered_eggs.values(): | ||
| 157 | raise KeyError("There is no such battle in the tournament") | 155 | raise KeyError("There is no such battle in the tournament") | ||
| 158 | if egg1.return_battle_winner(egg2, which_half) is None: | 156 | if egg1.return_battle_winner(egg2, which_half) is None: | ||
| 159 | raise KeyError("There is no such battle in the tournament") | 157 | raise KeyError("There is no such battle in the tournament") | ||
| 160 | return egg1.return_battle_winner(egg2, which_half) | 158 | return egg1.return_battle_winner(egg2, which_half) | ||
| 161 | 159 | ||||
| 162 | def __rmatmul__(self, position): | 160 | def __rmatmul__(self, position): | ||
| 163 | if position in self.__ranking.keys(): | 161 | if position in self.__ranking.keys(): | ||
| 164 | return {self.__registered_eggs[name] for name in self.__ranking[position]} | 162 | return {self.__registered_eggs[name] for name in self.__ranking[position]} | ||
| 165 | else: | 163 | else: | ||
| 166 | raise IndexError("There is no such position in the ranking table") | 164 | raise IndexError("There is no such position in the ranking table") | ||
| 167 | 165 | ||||
| 168 | def __getattr__(self, name): | 166 | def __getattr__(self, name): | ||
| 169 | for pos in self.__ranking.keys(): | 167 | for pos in self.__ranking.keys(): | ||
| 170 | if name in self.__ranking[pos]: | 168 | if name in self.__ranking[pos]: | ||
| 171 | rank = pos | 169 | rank = pos | ||
| 172 | break | 170 | break | ||
| 173 | return {"position": rank, "victories": self.__registered_eggs[name].victories_in_tournament} if name in self.__registered_eggs else AttributeError(f"Apologies, there is no such egg registered") | 171 | return {"position": rank, "victories": self.__registered_eggs[name].victories_in_tournament} if name in self.__registered_eggs else AttributeError(f"Apologies, there is no such egg registered") | ||
| 174 | 172 | ||||
| 175 | def __contains__(self, egg): | 173 | def __contains__(self, egg): | ||
| 176 | return egg in self.__registered_eggs.values() | 174 | return egg in self.__registered_eggs.values() |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
15.04.2026 15:54