1import keyword
2from enum import Enum
3from typing import Protocol
4
5EGG_SIDE_SIZE = EGG_SIDE_BOUNDARY = 50.0
6MAX_FILL = 100.0
7RGB_PAIR_COUNT = 3
8HEX_BASE = 16
9
10
11class Side(Enum):
12 TOP = (0.0, EGG_SIDE_BOUNDARY)
13 BOTTOM = (EGG_SIDE_BOUNDARY, MAX_FILL)
14
15 def __init__(self, low, high):
16 self.low = low
17 self.high = high
18
19 @classmethod
20 def from_str(cls, s):
21 try:
22 return cls[s.upper()]
23 except KeyError:
24 raise KeyError(f"Side must be one of {[m.lower() for m in cls._member_names_]}")
25
26
27class CollisionObserver(Protocol):
28 def __call__(self, e1: "Egg", e2: "Egg", side: Side, winner: "Egg") -> None:
29 pass
30
31
32class PaintLayer:
33 def __init__(self, rgb, fill_start, fill_end):
34 self.rgb = rgb
35 self.fill_start = fill_start
36 self.fill_end = fill_end
37
38 def overlap_with(self, side):
39 return min(self.fill_end, side.high) - max(self.fill_start, side.low)
40
41 def pigment_for(self, side):
42 overlap = self.overlap_with(side)
43 return self.rgb * (max(overlap, 0) / EGG_SIDE_SIZE)
44
45
46class EggSide:
47 def __init__(self):
48 self.__pigment = 0.0
49 self.__has_paint = False
50 self.__broken = False
51
52 def add_pigment(self, amount):
53 self.__has_paint = True
54 self.__pigment += amount
55
56 def is_stronger_than(self, other):
57 if self.__has_paint and not other.__has_paint:
58 return True
59 if other.__has_paint and not self.__has_paint:
60 return False
61 return self.__pigment >= other.__pigment
62
63 @property
64 def broken(self):
65 return self.__broken
66
67 def smash(self):
68 self.__broken = True
69
70
71class Egg:
72 def __init__(self):
73 self.__sides = {s: EggSide() for s in Side}
74 self.__paint_level = 0.0
75 self.__observer: CollisionObserver | None = None
76
77 def __mul__(self, other):
78 return self._collide(other, Side.TOP)
79
80 def __matmul__(self, other):
81 return self._collide(other, Side.BOTTOM)
82
83 def paint(self, *args):
84 if self.__paint_level + sum(amount for _, amount in args) > MAX_FILL:
85 raise ValueError("Paint overfill!")
86
87 for color, amount in args:
88 layer = PaintLayer(_parse_rgb(color), self.__paint_level, self.__paint_level + amount)
89 for side in Side:
90 if layer.overlap_with(side) > 0:
91 self.__sides[side].add_pigment(layer.pigment_for(side))
92 self.__paint_level += amount
93
94 def set_observer(self, observer: CollisionObserver):
95 if self.__observer is not None:
96 raise ValueError("An egg cannot be registered in multiple tournaments")
97 self.__observer = observer
98
99 def _collide(self, other, side):
100 if self.__sides[side].broken or other.__sides[side].broken:
101 raise TypeError("Cannot collide on a broken side")
102
103 # Battle
104 winner, loser = self._resolve_collision(other, side)
105 loser.__sides[side].smash()
106
107 # Notify
108 if self.__observer:
109 self.__observer(self, other, side, winner)
110
111 return winner
112
113 def _resolve_collision(self, other, side):
114 my_side = self.__sides[side]
115 their_side = other.__sides[side]
116 return (self, other) if my_side.is_stronger_than(their_side) else (other, self)
117
118
119def _parse_rgb(color):
120 return sum(int(color[i * 2: i * 2 + 2], HEX_BASE) for i in range(RGB_PAIR_COUNT))
121
122
123class EggTournament:
124 def __init__(self):
125 # Eggs
126 self.__eggs_by_name: dict[str, Egg] = {}
127 self.__registered_eggs: set[Egg] = set()
128
129 # Ranking
130 self.__wins: dict[Egg, int] = {}
131 self.__ranking_cache = None
132
133 # History
134 self.__battles: dict[tuple[frozenset, Side], Egg] = {}
135
136 def __getitem__(self, key):
137 if isinstance(key, slice):
138 e1, e2, side_str = key.start, key.stop, key.step
139 elif isinstance(key, tuple) and len(key) == 3:
140 e1, e2, side_str = key
141 else:
142 raise KeyError("Use tournament[e1, e2, side] or tournament[e1:e2:side]")
143
144 side = Side.from_str(side_str)
145 battle_key = (frozenset({e1, e2}), side)
146
147 if battle_key not in self.__battles:
148 raise KeyError("No such battle recorded")
149
150 return self.__battles[battle_key]
151
152 def __rmatmul__(self, position):
153 if not self.__wins:
154 raise IndexError("No eggs registered")
155
156 unique_sorted = self._sorted_unique_wins()
157 if position < 1 or position > len(unique_sorted):
158 raise IndexError(f"Position {position} does not exist in the ranking")
159
160 target = unique_sorted[position - 1]
161 result = {egg for egg, wins in self.__wins.items() if wins == target}
162 return result.pop() if len(result) == 1 else result
163
164 def __getattr__(self, name):
165 if name in self.__eggs_by_name:
166 egg = self.__eggs_by_name[name]
167 victories = self.__wins[egg]
168 position = self._sorted_unique_wins().index(victories) + 1
169 return {"position": position, "victories": victories}
170
171 raise AttributeError("Apologies, there is no such egg registered")
172
173 def __contains__(self, egg):
174 return egg in self.__registered_eggs
175
176 def register(self, egg, name):
177 if not name.isidentifier() or keyword.iskeyword(name):
178 raise ValueError("Invalid registration name")
179 if name in self.__eggs_by_name:
180 raise ValueError(f"Egg with name {name} has already been registered")
181 if egg in self.__registered_eggs:
182 raise ValueError("An egg cannot be registered in multiple tournaments")
183
184 egg.set_observer(self._record_battle)
185 self.__eggs_by_name[name] = egg
186 self.__registered_eggs.add(egg)
187 self.__wins[egg] = 0
188
189 def _record_battle(self, e1, e2, side, winner):
190 if e1 not in self.__registered_eggs or e2 not in self.__registered_eggs:
191 return
192
193 key = (frozenset({e1, e2}), side)
194 if key in self.__battles:
195 self.__wins[self.__battles[key]] -= 1
196
197 self.__battles[key] = winner
198 self.__wins[winner] += 1
199 self.__ranking_cache = None
200
201 def _sorted_unique_wins(self):
202 if self.__ranking_cache is None:
203 self.__ranking_cache = sorted(set(self.__wins.values()), reverse=True)
204 return self.__ranking_cache
..............................................
----------------------------------------------------------------------
Ran 46 tests in 0.002s
OK
Илиян Гаврилов
13.04.2026 17:17Еми доста време мъчих Egg класа и беше ужасно объркващо, та като абсолютен минимум да се разпредели малко сложна логика, мисля, че поне PaintLayer класът беше нужен като абстракция
|
Виктор Бечев
12.04.2026 22:08Интересни решения за някои от нещата. С един от преподавателите си говорихме за това дали си заслужава да се правят обекти за цветовете, ти си направил и за слоевете. На ръба между ненужно сложно и добър дизайн, но на мен по-скоро ми допада.
|
| f | 1 | import keyword | f | 1 | import keyword |
| 2 | from enum import Enum | 2 | from enum import Enum | ||
| 3 | from typing import Protocol | 3 | from typing import Protocol | ||
| 4 | 4 | ||||
| 5 | EGG_SIDE_SIZE = EGG_SIDE_BOUNDARY = 50.0 | 5 | EGG_SIDE_SIZE = EGG_SIDE_BOUNDARY = 50.0 | ||
| 6 | MAX_FILL = 100.0 | 6 | MAX_FILL = 100.0 | ||
| 7 | RGB_PAIR_COUNT = 3 | 7 | RGB_PAIR_COUNT = 3 | ||
| 8 | HEX_BASE = 16 | 8 | HEX_BASE = 16 | ||
| 9 | 9 | ||||
| 10 | 10 | ||||
| 11 | class Side(Enum): | 11 | class Side(Enum): | ||
| 12 | TOP = (0.0, EGG_SIDE_BOUNDARY) | 12 | TOP = (0.0, EGG_SIDE_BOUNDARY) | ||
| 13 | BOTTOM = (EGG_SIDE_BOUNDARY, MAX_FILL) | 13 | BOTTOM = (EGG_SIDE_BOUNDARY, MAX_FILL) | ||
| 14 | 14 | ||||
| 15 | def __init__(self, low, high): | 15 | def __init__(self, low, high): | ||
| 16 | self.low = low | 16 | self.low = low | ||
| 17 | self.high = high | 17 | self.high = high | ||
| 18 | 18 | ||||
| 19 | @classmethod | 19 | @classmethod | ||
| 20 | def from_str(cls, s): | 20 | def from_str(cls, s): | ||
| 21 | try: | 21 | try: | ||
| 22 | return cls[s.upper()] | 22 | return cls[s.upper()] | ||
| 23 | except KeyError: | 23 | except KeyError: | ||
| 24 | raise KeyError(f"Side must be one of {[m.lower() for m in cls._member_names_]}") | 24 | raise KeyError(f"Side must be one of {[m.lower() for m in cls._member_names_]}") | ||
| 25 | 25 | ||||
| 26 | 26 | ||||
| 27 | class CollisionObserver(Protocol): | 27 | class CollisionObserver(Protocol): | ||
| 28 | def __call__(self, e1: "Egg", e2: "Egg", side: Side, winner: "Egg") -> None: | 28 | def __call__(self, e1: "Egg", e2: "Egg", side: Side, winner: "Egg") -> None: | ||
| 29 | pass | 29 | pass | ||
| 30 | 30 | ||||
| 31 | 31 | ||||
| 32 | class PaintLayer: | 32 | class PaintLayer: | ||
| 33 | def __init__(self, rgb, fill_start, fill_end): | 33 | def __init__(self, rgb, fill_start, fill_end): | ||
| 34 | self.rgb = rgb | 34 | self.rgb = rgb | ||
| 35 | self.fill_start = fill_start | 35 | self.fill_start = fill_start | ||
| 36 | self.fill_end = fill_end | 36 | self.fill_end = fill_end | ||
| 37 | 37 | ||||
| 38 | def overlap_with(self, side): | 38 | def overlap_with(self, side): | ||
| 39 | return min(self.fill_end, side.high) - max(self.fill_start, side.low) | 39 | return min(self.fill_end, side.high) - max(self.fill_start, side.low) | ||
| 40 | 40 | ||||
| 41 | def pigment_for(self, side): | 41 | def pigment_for(self, side): | ||
| 42 | overlap = self.overlap_with(side) | 42 | overlap = self.overlap_with(side) | ||
| n | 43 | return self.rgb * (overlap / EGG_SIDE_SIZE) if overlap > 0 else 0.0 | n | 43 | return self.rgb * (max(overlap, 0) / EGG_SIDE_SIZE) |
| 44 | 44 | ||||
| 45 | 45 | ||||
| 46 | class EggSide: | 46 | class EggSide: | ||
| 47 | def __init__(self): | 47 | def __init__(self): | ||
| 48 | self.__pigment = 0.0 | 48 | self.__pigment = 0.0 | ||
| 49 | self.__has_paint = False | 49 | self.__has_paint = False | ||
| 50 | self.__broken = False | 50 | self.__broken = False | ||
| 51 | 51 | ||||
| 52 | def add_pigment(self, amount): | 52 | def add_pigment(self, amount): | ||
| 53 | self.__has_paint = True | 53 | self.__has_paint = True | ||
| 54 | self.__pigment += amount | 54 | self.__pigment += amount | ||
| 55 | 55 | ||||
| 56 | def is_stronger_than(self, other): | 56 | def is_stronger_than(self, other): | ||
| 57 | if self.__has_paint and not other.__has_paint: | 57 | if self.__has_paint and not other.__has_paint: | ||
| 58 | return True | 58 | return True | ||
| 59 | if other.__has_paint and not self.__has_paint: | 59 | if other.__has_paint and not self.__has_paint: | ||
| 60 | return False | 60 | return False | ||
| 61 | return self.__pigment >= other.__pigment | 61 | return self.__pigment >= other.__pigment | ||
| 62 | 62 | ||||
| 63 | @property | 63 | @property | ||
| 64 | def broken(self): | 64 | def broken(self): | ||
| 65 | return self.__broken | 65 | return self.__broken | ||
| 66 | 66 | ||||
| 67 | def smash(self): | 67 | def smash(self): | ||
| 68 | self.__broken = True | 68 | self.__broken = True | ||
| 69 | 69 | ||||
| 70 | 70 | ||||
| 71 | class Egg: | 71 | class Egg: | ||
| 72 | def __init__(self): | 72 | def __init__(self): | ||
| 73 | self.__sides = {s: EggSide() for s in Side} | 73 | self.__sides = {s: EggSide() for s in Side} | ||
| 74 | self.__paint_level = 0.0 | 74 | self.__paint_level = 0.0 | ||
| 75 | self.__observer: CollisionObserver | None = None | 75 | self.__observer: CollisionObserver | None = None | ||
| 76 | 76 | ||||
| 77 | def __mul__(self, other): | 77 | def __mul__(self, other): | ||
| 78 | return self._collide(other, Side.TOP) | 78 | return self._collide(other, Side.TOP) | ||
| 79 | 79 | ||||
| 80 | def __matmul__(self, other): | 80 | def __matmul__(self, other): | ||
| 81 | return self._collide(other, Side.BOTTOM) | 81 | return self._collide(other, Side.BOTTOM) | ||
| 82 | 82 | ||||
| 83 | def paint(self, *args): | 83 | def paint(self, *args): | ||
| 84 | if self.__paint_level + sum(amount for _, amount in args) > MAX_FILL: | 84 | if self.__paint_level + sum(amount for _, amount in args) > MAX_FILL: | ||
| 85 | raise ValueError("Paint overfill!") | 85 | raise ValueError("Paint overfill!") | ||
| 86 | 86 | ||||
| 87 | for color, amount in args: | 87 | for color, amount in args: | ||
| 88 | layer = PaintLayer(_parse_rgb(color), self.__paint_level, self.__paint_level + amount) | 88 | layer = PaintLayer(_parse_rgb(color), self.__paint_level, self.__paint_level + amount) | ||
| 89 | for side in Side: | 89 | for side in Side: | ||
| 90 | if layer.overlap_with(side) > 0: | 90 | if layer.overlap_with(side) > 0: | ||
| 91 | self.__sides[side].add_pigment(layer.pigment_for(side)) | 91 | self.__sides[side].add_pigment(layer.pigment_for(side)) | ||
| 92 | self.__paint_level += amount | 92 | self.__paint_level += amount | ||
| 93 | 93 | ||||
| 94 | def set_observer(self, observer: CollisionObserver): | 94 | def set_observer(self, observer: CollisionObserver): | ||
| 95 | if self.__observer is not None: | 95 | if self.__observer is not None: | ||
| 96 | raise ValueError("An egg cannot be registered in multiple tournaments") | 96 | raise ValueError("An egg cannot be registered in multiple tournaments") | ||
| 97 | self.__observer = observer | 97 | self.__observer = observer | ||
| 98 | 98 | ||||
| 99 | def _collide(self, other, side): | 99 | def _collide(self, other, side): | ||
| 100 | if self.__sides[side].broken or other.__sides[side].broken: | 100 | if self.__sides[side].broken or other.__sides[side].broken: | ||
| 101 | raise TypeError("Cannot collide on a broken side") | 101 | raise TypeError("Cannot collide on a broken side") | ||
| 102 | 102 | ||||
| 103 | # Battle | 103 | # Battle | ||
| 104 | winner, loser = self._resolve_collision(other, side) | 104 | winner, loser = self._resolve_collision(other, side) | ||
| 105 | loser.__sides[side].smash() | 105 | loser.__sides[side].smash() | ||
| 106 | 106 | ||||
| 107 | # Notify | 107 | # Notify | ||
| 108 | if self.__observer: | 108 | if self.__observer: | ||
| 109 | self.__observer(self, other, side, winner) | 109 | self.__observer(self, other, side, winner) | ||
| 110 | 110 | ||||
| 111 | return winner | 111 | return winner | ||
| 112 | 112 | ||||
| 113 | def _resolve_collision(self, other, side): | 113 | def _resolve_collision(self, other, side): | ||
| 114 | my_side = self.__sides[side] | 114 | my_side = self.__sides[side] | ||
| 115 | their_side = other.__sides[side] | 115 | their_side = other.__sides[side] | ||
| 116 | return (self, other) if my_side.is_stronger_than(their_side) else (other, self) | 116 | return (self, other) if my_side.is_stronger_than(their_side) else (other, self) | ||
| 117 | 117 | ||||
| 118 | 118 | ||||
| 119 | def _parse_rgb(color): | 119 | def _parse_rgb(color): | ||
| n | 120 | c = color.upper() | n | ||
| 121 | return sum(int(c[i * 2: i * 2 + 2], HEX_BASE) for i in range(RGB_PAIR_COUNT)) | 120 | return sum(int(color[i * 2: i * 2 + 2], HEX_BASE) for i in range(RGB_PAIR_COUNT)) | ||
| 122 | 121 | ||||
| 123 | 122 | ||||
| 124 | class EggTournament: | 123 | class EggTournament: | ||
| 125 | def __init__(self): | 124 | def __init__(self): | ||
| 126 | # Eggs | 125 | # Eggs | ||
| 127 | self.__eggs_by_name: dict[str, Egg] = {} | 126 | self.__eggs_by_name: dict[str, Egg] = {} | ||
| 128 | self.__registered_eggs: set[Egg] = set() | 127 | self.__registered_eggs: set[Egg] = set() | ||
| 129 | 128 | ||||
| 130 | # Ranking | 129 | # Ranking | ||
| 131 | self.__wins: dict[Egg, int] = {} | 130 | self.__wins: dict[Egg, int] = {} | ||
| 132 | self.__ranking_cache = None | 131 | self.__ranking_cache = None | ||
| 133 | 132 | ||||
| 134 | # History | 133 | # History | ||
| 135 | self.__battles: dict[tuple[frozenset, Side], Egg] = {} | 134 | self.__battles: dict[tuple[frozenset, Side], Egg] = {} | ||
| 136 | 135 | ||||
| 137 | def __getitem__(self, key): | 136 | def __getitem__(self, key): | ||
| 138 | if isinstance(key, slice): | 137 | if isinstance(key, slice): | ||
| 139 | e1, e2, side_str = key.start, key.stop, key.step | 138 | e1, e2, side_str = key.start, key.stop, key.step | ||
| 140 | elif isinstance(key, tuple) and len(key) == 3: | 139 | elif isinstance(key, tuple) and len(key) == 3: | ||
| 141 | e1, e2, side_str = key | 140 | e1, e2, side_str = key | ||
| 142 | else: | 141 | else: | ||
| 143 | raise KeyError("Use tournament[e1, e2, side] or tournament[e1:e2:side]") | 142 | raise KeyError("Use tournament[e1, e2, side] or tournament[e1:e2:side]") | ||
| 144 | 143 | ||||
| 145 | side = Side.from_str(side_str) | 144 | side = Side.from_str(side_str) | ||
| 146 | battle_key = (frozenset({e1, e2}), side) | 145 | battle_key = (frozenset({e1, e2}), side) | ||
| 147 | 146 | ||||
| 148 | if battle_key not in self.__battles: | 147 | if battle_key not in self.__battles: | ||
| 149 | raise KeyError("No such battle recorded") | 148 | raise KeyError("No such battle recorded") | ||
| 150 | 149 | ||||
| 151 | return self.__battles[battle_key] | 150 | return self.__battles[battle_key] | ||
| 152 | 151 | ||||
| 153 | def __rmatmul__(self, position): | 152 | def __rmatmul__(self, position): | ||
| 154 | if not self.__wins: | 153 | if not self.__wins: | ||
| 155 | raise IndexError("No eggs registered") | 154 | raise IndexError("No eggs registered") | ||
| 156 | 155 | ||||
| 157 | unique_sorted = self._sorted_unique_wins() | 156 | unique_sorted = self._sorted_unique_wins() | ||
| 158 | if position < 1 or position > len(unique_sorted): | 157 | if position < 1 or position > len(unique_sorted): | ||
| 159 | raise IndexError(f"Position {position} does not exist in the ranking") | 158 | raise IndexError(f"Position {position} does not exist in the ranking") | ||
| 160 | 159 | ||||
| 161 | target = unique_sorted[position - 1] | 160 | target = unique_sorted[position - 1] | ||
| 162 | result = {egg for egg, wins in self.__wins.items() if wins == target} | 161 | result = {egg for egg, wins in self.__wins.items() if wins == target} | ||
| 163 | return result.pop() if len(result) == 1 else result | 162 | return result.pop() if len(result) == 1 else result | ||
| 164 | 163 | ||||
| 165 | def __getattr__(self, name): | 164 | def __getattr__(self, name): | ||
| t | 166 | eggs = object.__getattribute__(self, "_EggTournament__eggs_by_name") | t | ||
| 167 | wins = object.__getattribute__(self, "_EggTournament__wins") | ||||
| 168 | |||||
| 169 | if name in eggs: | 165 | if name in self.__eggs_by_name: | ||
| 170 | egg = eggs[name] | 166 | egg = self.__eggs_by_name[name] | ||
| 171 | victories = wins[egg] | 167 | victories = self.__wins[egg] | ||
| 172 | position = self._sorted_unique_wins().index(victories) + 1 | 168 | position = self._sorted_unique_wins().index(victories) + 1 | ||
| 173 | return {"position": position, "victories": victories} | 169 | return {"position": position, "victories": victories} | ||
| 174 | 170 | ||||
| 175 | raise AttributeError("Apologies, there is no such egg registered") | 171 | raise AttributeError("Apologies, there is no such egg registered") | ||
| 176 | 172 | ||||
| 177 | def __contains__(self, egg): | 173 | def __contains__(self, egg): | ||
| 178 | return egg in self.__registered_eggs | 174 | return egg in self.__registered_eggs | ||
| 179 | 175 | ||||
| 180 | def register(self, egg, name): | 176 | def register(self, egg, name): | ||
| 181 | if not name.isidentifier() or keyword.iskeyword(name): | 177 | if not name.isidentifier() or keyword.iskeyword(name): | ||
| 182 | raise ValueError("Invalid registration name") | 178 | raise ValueError("Invalid registration name") | ||
| 183 | if name in self.__eggs_by_name: | 179 | if name in self.__eggs_by_name: | ||
| 184 | raise ValueError(f"Egg with name {name} has already been registered") | 180 | raise ValueError(f"Egg with name {name} has already been registered") | ||
| 185 | if egg in self.__registered_eggs: | 181 | if egg in self.__registered_eggs: | ||
| 186 | raise ValueError("An egg cannot be registered in multiple tournaments") | 182 | raise ValueError("An egg cannot be registered in multiple tournaments") | ||
| 187 | 183 | ||||
| 188 | egg.set_observer(self._record_battle) | 184 | egg.set_observer(self._record_battle) | ||
| 189 | self.__eggs_by_name[name] = egg | 185 | self.__eggs_by_name[name] = egg | ||
| 190 | self.__registered_eggs.add(egg) | 186 | self.__registered_eggs.add(egg) | ||
| 191 | self.__wins[egg] = 0 | 187 | self.__wins[egg] = 0 | ||
| 192 | 188 | ||||
| 193 | def _record_battle(self, e1, e2, side, winner): | 189 | def _record_battle(self, e1, e2, side, winner): | ||
| 194 | if e1 not in self.__registered_eggs or e2 not in self.__registered_eggs: | 190 | if e1 not in self.__registered_eggs or e2 not in self.__registered_eggs: | ||
| 195 | return | 191 | return | ||
| 196 | 192 | ||||
| 197 | key = (frozenset({e1, e2}), side) | 193 | key = (frozenset({e1, e2}), side) | ||
| 198 | if key in self.__battles: | 194 | if key in self.__battles: | ||
| 199 | self.__wins[self.__battles[key]] -= 1 | 195 | self.__wins[self.__battles[key]] -= 1 | ||
| 200 | 196 | ||||
| 201 | self.__battles[key] = winner | 197 | self.__battles[key] = winner | ||
| 202 | self.__wins[winner] += 1 | 198 | self.__wins[winner] += 1 | ||
| 203 | self.__ranking_cache = None | 199 | self.__ranking_cache = None | ||
| 204 | 200 | ||||
| 205 | def _sorted_unique_wins(self): | 201 | def _sorted_unique_wins(self): | ||
| 206 | if self.__ranking_cache is None: | 202 | if self.__ranking_cache is None: | ||
| 207 | self.__ranking_cache = sorted(set(self.__wins.values()), reverse=True) | 203 | self.__ranking_cache = sorted(set(self.__wins.values()), reverse=True) | ||
| 208 | return self.__ranking_cache | 204 | return self.__ranking_cache |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||