Домашни > Великденско домашно > Решения > Решението на Виктор Бечев

Резултати
6 точки от тестове
0 точки от учител

6 точки общо

46 успешни теста
0 неуспешни теста
Код (Final fixes)
Скрий всички коментари

  1from enum import StrEnum
  2
  3
  4class EggSide(StrEnum):
  5    top = "top"
  6    bottom = "bottom"
  7
  8
  9class Egg:
 10    def __init__(self):
 11        self.top_coloring = []
 12        self.bottom_coloring = []
 13        self.broken_sides = []
 14
 15    def paint(self, *colors):
 16        total_coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring + list(colors))
 17        if total_coverage > 100:
 18            raise ValueError(f"Cannot overpaint egg, egg is already at {total_coverage}%")
 19        for color, percentage in colors:
 20            coverage_top = sum(percentage for _, percentage in self.top_coloring)
 21            if leftover_coverage_top := 50 - coverage_top:
 22                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))
 23            if coverage_top + percentage > 50:
 24                self.bottom_coloring.append((color, percentage - leftover_coverage_top))
 25
 26    def _calculate_hardness(self, side):
 27        return sum(
 28            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage
 29            for color, percentage in getattr(self, f"{side}_coloring")
 30        )
 31
 32    @property
 33    def top_hardness(self):
 34        return self._calculate_hardness(EggSide.top)
 35
 36    @property
 37    def bottom_hardness(self):
 38        return self._calculate_hardness(EggSide.bottom)
 39
 40    def break_side(self, side):
 41        self.broken_sides.append(side)
 42
 43    def _collide_eggs(self, other, side):
 44        if side in self.broken_sides or side in other.broken_sides:
 45            raise TypeError("Cannot collide with broken sides")
 46        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):
 47            winner, loser = self, other
 48        else:
 49            winner, loser = other, self
 50        loser.break_side(side)
 51        EggTournament.log_fight(self, other, side, winner)
 52        return winner
 53
 54    def __mul__(self, other):
 55        return self._collide_eggs(other, EggSide.top)
 56
 57    def __matmul__(self, other):
 58        return self._collide_eggs(other, EggSide.bottom)
 59
 60
 61class EggTournament:
 62    instances = set()
 63
 64    def __init__(self):
 65        self.instances.add(self)
 66        self.registered_eggs = {}
 67        self.fight_history = {}
 68
 69    def register(self, egg, name):
 70        if name in self.registered_eggs:
 71            raise ValueError(f"Egg with name {name} has already been registered")
 72        if not name.isidentifier():
 73            raise ValueError("Invalid registration name")
 74        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):
 75            raise ValueError("An egg cannot be registered in multiple tournaments")
 76        self.registered_eggs[name] = egg
 77
 78    @classmethod
 79    def log_fight(cls, first, second, side, winner):
 80        for tournament in cls.instances:
 81            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():
 82                tournament.fight_history[(first, second, side)] = winner
 83
 84    @property
 85    def egg_wins(self):
 86        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}
 87
 88    @property
 89    def ranking(self):
 90        return [
 91            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}
 92            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)
 93        ]
 94
 95    def __getattr__(self, name):
 96        try:
 97            egg = self.registered_eggs[name]
 98        except KeyError:
 99            raise AttributeError("Apologies, there is no such egg registered")
100        for rank, eggs in enumerate(self.ranking, start=1):
101            if egg in eggs:
102                return {"position": rank, "victories": self.egg_wins[egg]}
103
104    def __getitem__(self, key):
105        if isinstance(key, slice):
106            key = key.start, key.stop, key.step
107        try:
108            return self.fight_history[key]
109        except KeyError:
110            return self.fight_history[key[1], key[0], key[2]]
111
112    def __rmatmul__(self, position):
113        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()
114
115    def __contains__(self, value):
116        return value in self.registered_eggs.values()

..............................................
----------------------------------------------------------------------
Ran 46 tests in 0.002s

OK

Дискусия
История

f1from enum import StrEnumf1from enum import StrEnum
22
33
4class EggSide(StrEnum):4class EggSide(StrEnum):
5    top = "top"5    top = "top"
6    bottom = "bottom"6    bottom = "bottom"
77
88
9class Egg:9class Egg:
10    def __init__(self):10    def __init__(self):
11        self.top_coloring = []11        self.top_coloring = []
12        self.bottom_coloring = []12        self.bottom_coloring = []
13        self.broken_sides = []13        self.broken_sides = []
1414
15    def paint(self, *colors):15    def paint(self, *colors):
t16        coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring) + sum(t16        total_coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring + list(colors))
17            percentage for _, percentage in colors
18        )
19        if coverage > 100:17        if total_coverage > 100:
20            raise ValueError(f"Cannot overpaint egg, egg is already at {coverage}%")18            raise ValueError(f"Cannot overpaint egg, egg is already at {total_coverage}%")
21        for color, percentage in colors:19        for color, percentage in colors:
22            coverage_top = sum(percentage for _, percentage in self.top_coloring)20            coverage_top = sum(percentage for _, percentage in self.top_coloring)
23            if leftover_coverage_top := 50 - coverage_top:21            if leftover_coverage_top := 50 - coverage_top:
24                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))22                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))
25            if coverage_top + percentage > 50:23            if coverage_top + percentage > 50:
26                self.bottom_coloring.append((color, percentage - leftover_coverage_top))24                self.bottom_coloring.append((color, percentage - leftover_coverage_top))
2725
28    def _calculate_hardness(self, side):26    def _calculate_hardness(self, side):
29        return sum(27        return sum(
30            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage28            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage
31            for color, percentage in getattr(self, f"{side}_coloring")29            for color, percentage in getattr(self, f"{side}_coloring")
32        )30        )
3331
34    @property32    @property
35    def top_hardness(self):33    def top_hardness(self):
36        return self._calculate_hardness(EggSide.top)34        return self._calculate_hardness(EggSide.top)
3735
38    @property36    @property
39    def bottom_hardness(self):37    def bottom_hardness(self):
40        return self._calculate_hardness(EggSide.bottom)38        return self._calculate_hardness(EggSide.bottom)
4139
42    def break_side(self, side):40    def break_side(self, side):
43        self.broken_sides.append(side)41        self.broken_sides.append(side)
4442
45    def _collide_eggs(self, other, side):43    def _collide_eggs(self, other, side):
46        if side in self.broken_sides or side in other.broken_sides:44        if side in self.broken_sides or side in other.broken_sides:
47            raise TypeError("Cannot collide with broken sides")45            raise TypeError("Cannot collide with broken sides")
48        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):46        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):
49            winner, loser = self, other47            winner, loser = self, other
50        else:48        else:
51            winner, loser = other, self49            winner, loser = other, self
52        loser.break_side(side)50        loser.break_side(side)
53        EggTournament.log_fight(self, other, side, winner)51        EggTournament.log_fight(self, other, side, winner)
54        return winner52        return winner
5553
56    def __mul__(self, other):54    def __mul__(self, other):
57        return self._collide_eggs(other, EggSide.top)55        return self._collide_eggs(other, EggSide.top)
5856
59    def __matmul__(self, other):57    def __matmul__(self, other):
60        return self._collide_eggs(other, EggSide.bottom)58        return self._collide_eggs(other, EggSide.bottom)
6159
6260
63class EggTournament:61class EggTournament:
64    instances = set()62    instances = set()
6563
66    def __init__(self):64    def __init__(self):
67        self.instances.add(self)65        self.instances.add(self)
68        self.registered_eggs = {}66        self.registered_eggs = {}
69        self.fight_history = {}67        self.fight_history = {}
7068
71    def register(self, egg, name):69    def register(self, egg, name):
72        if name in self.registered_eggs:70        if name in self.registered_eggs:
73            raise ValueError(f"Egg with name {name} has already been registered")71            raise ValueError(f"Egg with name {name} has already been registered")
74        if not name.isidentifier():72        if not name.isidentifier():
75            raise ValueError("Invalid registration name")73            raise ValueError("Invalid registration name")
76        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):74        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):
77            raise ValueError("An egg cannot be registered in multiple tournaments")75            raise ValueError("An egg cannot be registered in multiple tournaments")
78        self.registered_eggs[name] = egg76        self.registered_eggs[name] = egg
7977
80    @classmethod78    @classmethod
81    def log_fight(cls, first, second, side, winner):79    def log_fight(cls, first, second, side, winner):
82        for tournament in cls.instances:80        for tournament in cls.instances:
83            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():81            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():
84                tournament.fight_history[(first, second, side)] = winner82                tournament.fight_history[(first, second, side)] = winner
8583
86    @property84    @property
87    def egg_wins(self):85    def egg_wins(self):
88        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}86        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}
8987
90    @property88    @property
91    def ranking(self):89    def ranking(self):
92        return [90        return [
93            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}91            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}
94            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)92            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)
95        ]93        ]
9694
97    def __getattr__(self, name):95    def __getattr__(self, name):
98        try:96        try:
99            egg = self.registered_eggs[name]97            egg = self.registered_eggs[name]
100        except KeyError:98        except KeyError:
101            raise AttributeError("Apologies, there is no such egg registered")99            raise AttributeError("Apologies, there is no such egg registered")
102        for rank, eggs in enumerate(self.ranking, start=1):100        for rank, eggs in enumerate(self.ranking, start=1):
103            if egg in eggs:101            if egg in eggs:
104                return {"position": rank, "victories": self.egg_wins[egg]}102                return {"position": rank, "victories": self.egg_wins[egg]}
105103
106    def __getitem__(self, key):104    def __getitem__(self, key):
107        if isinstance(key, slice):105        if isinstance(key, slice):
108            key = key.start, key.stop, key.step106            key = key.start, key.stop, key.step
109        try:107        try:
110            return self.fight_history[key]108            return self.fight_history[key]
111        except KeyError:109        except KeyError:
112            return self.fight_history[key[1], key[0], key[2]]110            return self.fight_history[key[1], key[0], key[2]]
113111
114    def __rmatmul__(self, position):112    def __rmatmul__(self, position):
115        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()113        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()
116114
117    def __contains__(self, value):115    def __contains__(self, value):
118        return value in self.registered_eggs.values()116        return value in self.registered_eggs.values()
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1from enum import StrEnumf1from enum import StrEnum
22
33
4class EggSide(StrEnum):4class EggSide(StrEnum):
5    top = "top"5    top = "top"
6    bottom = "bottom"6    bottom = "bottom"
77
88
9class Egg:9class Egg:
10    def __init__(self):10    def __init__(self):
11        self.top_coloring = []11        self.top_coloring = []
12        self.bottom_coloring = []12        self.bottom_coloring = []
13        self.broken_sides = []13        self.broken_sides = []
1414
15    def paint(self, *colors):15    def paint(self, *colors):
nn16        coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring) + sum(
17            percentage for _, percentage in colors
18        )
19        if coverage > 100:
20            raise ValueError(f"Cannot overpaint egg, egg is already at {coverage}%")
16        for color, percentage in colors:21        for color, percentage in colors:
t17            coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring) + percentaget
18            if coverage > 100:
19                raise ValueError(f"Cannot overpaint egg, egg is already at {coverage}%")
20            coverage_top = sum(percentage for _, percentage in self.top_coloring)22            coverage_top = sum(percentage for _, percentage in self.top_coloring)
21            if leftover_coverage_top := 50 - coverage_top:23            if leftover_coverage_top := 50 - coverage_top:
22                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))24                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))
23            if coverage_top + percentage > 50:25            if coverage_top + percentage > 50:
24                self.bottom_coloring.append((color, percentage - leftover_coverage_top))26                self.bottom_coloring.append((color, percentage - leftover_coverage_top))
2527
26    def _calculate_hardness(self, side):28    def _calculate_hardness(self, side):
27        return sum(29        return sum(
28            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage30            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage
29            for color, percentage in getattr(self, f"{side}_coloring")31            for color, percentage in getattr(self, f"{side}_coloring")
30        )32        )
3133
32    @property34    @property
33    def top_hardness(self):35    def top_hardness(self):
34        return self._calculate_hardness(EggSide.top)36        return self._calculate_hardness(EggSide.top)
3537
36    @property38    @property
37    def bottom_hardness(self):39    def bottom_hardness(self):
38        return self._calculate_hardness(EggSide.bottom)40        return self._calculate_hardness(EggSide.bottom)
3941
40    def break_side(self, side):42    def break_side(self, side):
41        self.broken_sides.append(side)43        self.broken_sides.append(side)
4244
43    def _collide_eggs(self, other, side):45    def _collide_eggs(self, other, side):
44        if side in self.broken_sides or side in other.broken_sides:46        if side in self.broken_sides or side in other.broken_sides:
45            raise TypeError("Cannot collide with broken sides")47            raise TypeError("Cannot collide with broken sides")
46        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):48        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):
47            winner, loser = self, other49            winner, loser = self, other
48        else:50        else:
49            winner, loser = other, self51            winner, loser = other, self
50        loser.break_side(side)52        loser.break_side(side)
51        EggTournament.log_fight(self, other, side, winner)53        EggTournament.log_fight(self, other, side, winner)
52        return winner54        return winner
5355
54    def __mul__(self, other):56    def __mul__(self, other):
55        return self._collide_eggs(other, EggSide.top)57        return self._collide_eggs(other, EggSide.top)
5658
57    def __matmul__(self, other):59    def __matmul__(self, other):
58        return self._collide_eggs(other, EggSide.bottom)60        return self._collide_eggs(other, EggSide.bottom)
5961
6062
61class EggTournament:63class EggTournament:
62    instances = set()64    instances = set()
6365
64    def __init__(self):66    def __init__(self):
65        self.instances.add(self)67        self.instances.add(self)
66        self.registered_eggs = {}68        self.registered_eggs = {}
67        self.fight_history = {}69        self.fight_history = {}
6870
69    def register(self, egg, name):71    def register(self, egg, name):
70        if name in self.registered_eggs:72        if name in self.registered_eggs:
71            raise ValueError(f"Egg with name {name} has already been registered")73            raise ValueError(f"Egg with name {name} has already been registered")
72        if not name.isidentifier():74        if not name.isidentifier():
73            raise ValueError("Invalid registration name")75            raise ValueError("Invalid registration name")
74        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):76        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):
75            raise ValueError("An egg cannot be registered in multiple tournaments")77            raise ValueError("An egg cannot be registered in multiple tournaments")
76        self.registered_eggs[name] = egg78        self.registered_eggs[name] = egg
7779
78    @classmethod80    @classmethod
79    def log_fight(cls, first, second, side, winner):81    def log_fight(cls, first, second, side, winner):
80        for tournament in cls.instances:82        for tournament in cls.instances:
81            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():83            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():
82                tournament.fight_history[(first, second, side)] = winner84                tournament.fight_history[(first, second, side)] = winner
8385
84    @property86    @property
85    def egg_wins(self):87    def egg_wins(self):
86        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}88        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}
8789
88    @property90    @property
89    def ranking(self):91    def ranking(self):
90        return [92        return [
91            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}93            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}
92            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)94            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)
93        ]95        ]
9496
95    def __getattr__(self, name):97    def __getattr__(self, name):
96        try:98        try:
97            egg = self.registered_eggs[name]99            egg = self.registered_eggs[name]
98        except KeyError:100        except KeyError:
99            raise AttributeError("Apologies, there is no such egg registered")101            raise AttributeError("Apologies, there is no such egg registered")
100        for rank, eggs in enumerate(self.ranking, start=1):102        for rank, eggs in enumerate(self.ranking, start=1):
101            if egg in eggs:103            if egg in eggs:
102                return {"position": rank, "victories": self.egg_wins[egg]}104                return {"position": rank, "victories": self.egg_wins[egg]}
103105
104    def __getitem__(self, key):106    def __getitem__(self, key):
105        if isinstance(key, slice):107        if isinstance(key, slice):
106            key = key.start, key.stop, key.step108            key = key.start, key.stop, key.step
107        try:109        try:
108            return self.fight_history[key]110            return self.fight_history[key]
109        except KeyError:111        except KeyError:
110            return self.fight_history[key[1], key[0], key[2]]112            return self.fight_history[key[1], key[0], key[2]]
111113
112    def __rmatmul__(self, position):114    def __rmatmul__(self, position):
113        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()115        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()
114116
115    def __contains__(self, value):117    def __contains__(self, value):
116        return value in self.registered_eggs.values()118        return value in self.registered_eggs.values()
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1from enum import StrEnumf1from enum import StrEnum
22
33
4class EggSide(StrEnum):4class EggSide(StrEnum):
5    top = "top"5    top = "top"
6    bottom = "bottom"6    bottom = "bottom"
77
88
9class Egg:9class Egg:
10    def __init__(self):10    def __init__(self):
11        self.top_coloring = []11        self.top_coloring = []
12        self.bottom_coloring = []12        self.bottom_coloring = []
13        self.broken_sides = []13        self.broken_sides = []
1414
15    def paint(self, *colors):15    def paint(self, *colors):
16        for color, percentage in colors:16        for color, percentage in colors:
17            coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring) + percentage17            coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring) + percentage
18            if coverage > 100:18            if coverage > 100:
19                raise ValueError(f"Cannot overpaint egg, egg is already at {coverage}%")19                raise ValueError(f"Cannot overpaint egg, egg is already at {coverage}%")
t20 t
21            coverage_top = sum(percentage for _, percentage in self.top_coloring)20            coverage_top = sum(percentage for _, percentage in self.top_coloring)
22            if leftover_coverage_top := 50 - coverage_top:21            if leftover_coverage_top := 50 - coverage_top:
23                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))22                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))
24            if coverage_top + percentage > 50:23            if coverage_top + percentage > 50:
25                self.bottom_coloring.append((color, percentage - leftover_coverage_top))24                self.bottom_coloring.append((color, percentage - leftover_coverage_top))
2625
27    def _calculate_hardness(self, side):26    def _calculate_hardness(self, side):
28        return sum(27        return sum(
29            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage28            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage
30            for color, percentage in getattr(self, f"{side}_coloring")29            for color, percentage in getattr(self, f"{side}_coloring")
31        )30        )
3231
33    @property32    @property
34    def top_hardness(self):33    def top_hardness(self):
35        return self._calculate_hardness(EggSide.top)34        return self._calculate_hardness(EggSide.top)
3635
37    @property36    @property
38    def bottom_hardness(self):37    def bottom_hardness(self):
39        return self._calculate_hardness(EggSide.bottom)38        return self._calculate_hardness(EggSide.bottom)
4039
41    def break_side(self, side):40    def break_side(self, side):
42        self.broken_sides.append(side)41        self.broken_sides.append(side)
4342
44    def _collide_eggs(self, other, side):43    def _collide_eggs(self, other, side):
45        if side in self.broken_sides or side in other.broken_sides:44        if side in self.broken_sides or side in other.broken_sides:
46            raise TypeError("Cannot collide with broken sides")45            raise TypeError("Cannot collide with broken sides")
47        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):46        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):
48            winner, loser = self, other47            winner, loser = self, other
49        else:48        else:
50            winner, loser = other, self49            winner, loser = other, self
51        loser.break_side(side)50        loser.break_side(side)
52        EggTournament.log_fight(self, other, side, winner)51        EggTournament.log_fight(self, other, side, winner)
53        return winner52        return winner
5453
55    def __mul__(self, other):54    def __mul__(self, other):
56        return self._collide_eggs(other, EggSide.top)55        return self._collide_eggs(other, EggSide.top)
5756
58    def __matmul__(self, other):57    def __matmul__(self, other):
59        return self._collide_eggs(other, EggSide.bottom)58        return self._collide_eggs(other, EggSide.bottom)
6059
6160
62class EggTournament:61class EggTournament:
63    instances = set()62    instances = set()
6463
65    def __init__(self):64    def __init__(self):
66        self.instances.add(self)65        self.instances.add(self)
67        self.registered_eggs = {}66        self.registered_eggs = {}
68        self.fight_history = {}67        self.fight_history = {}
6968
70    def register(self, egg, name):69    def register(self, egg, name):
71        if name in self.registered_eggs:70        if name in self.registered_eggs:
72            raise ValueError(f"Egg with name {name} has already been registered")71            raise ValueError(f"Egg with name {name} has already been registered")
73        if not name.isidentifier():72        if not name.isidentifier():
74            raise ValueError("Invalid registration name")73            raise ValueError("Invalid registration name")
75        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):74        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):
76            raise ValueError("An egg cannot be registered in multiple tournaments")75            raise ValueError("An egg cannot be registered in multiple tournaments")
77        self.registered_eggs[name] = egg76        self.registered_eggs[name] = egg
7877
79    @classmethod78    @classmethod
80    def log_fight(cls, first, second, side, winner):79    def log_fight(cls, first, second, side, winner):
81        for tournament in cls.instances:80        for tournament in cls.instances:
82            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():81            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():
83                tournament.fight_history[(first, second, side)] = winner82                tournament.fight_history[(first, second, side)] = winner
8483
85    @property84    @property
86    def egg_wins(self):85    def egg_wins(self):
87        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}86        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}
8887
89    @property88    @property
90    def ranking(self):89    def ranking(self):
91        return [90        return [
92            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}91            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}
93            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)92            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)
94        ]93        ]
9594
96    def __getattr__(self, name):95    def __getattr__(self, name):
97        try:96        try:
98            egg = self.registered_eggs[name]97            egg = self.registered_eggs[name]
99        except KeyError:98        except KeyError:
100            raise AttributeError("Apologies, there is no such egg registered")99            raise AttributeError("Apologies, there is no such egg registered")
101        for rank, eggs in enumerate(self.ranking, start=1):100        for rank, eggs in enumerate(self.ranking, start=1):
102            if egg in eggs:101            if egg in eggs:
103                return {"position": rank, "victories": self.egg_wins[egg]}102                return {"position": rank, "victories": self.egg_wins[egg]}
104103
105    def __getitem__(self, key):104    def __getitem__(self, key):
106        if isinstance(key, slice):105        if isinstance(key, slice):
107            key = key.start, key.stop, key.step106            key = key.start, key.stop, key.step
108        try:107        try:
109            return self.fight_history[key]108            return self.fight_history[key]
110        except KeyError:109        except KeyError:
111            return self.fight_history[key[1], key[0], key[2]]110            return self.fight_history[key[1], key[0], key[2]]
112111
113    def __rmatmul__(self, position):112    def __rmatmul__(self, position):
114        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()113        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()
115114
116    def __contains__(self, value):115    def __contains__(self, value):
117        return value in self.registered_eggs.values()116        return value in self.registered_eggs.values()
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1from enum import StrEnumf1from enum import StrEnum
22
33
4class EggSide(StrEnum):4class EggSide(StrEnum):
5    top = "top"5    top = "top"
6    bottom = "bottom"6    bottom = "bottom"
77
88
9class Egg:9class Egg:
10    def __init__(self):10    def __init__(self):
11        self.top_coloring = []11        self.top_coloring = []
12        self.bottom_coloring = []12        self.bottom_coloring = []
13        self.broken_sides = []13        self.broken_sides = []
1414
15    def paint(self, *colors):15    def paint(self, *colors):
16        for color, percentage in colors:16        for color, percentage in colors:
17            coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring) + percentage17            coverage = sum(percentage for _, percentage in self.top_coloring + self.bottom_coloring) + percentage
18            if coverage > 100:18            if coverage > 100:
19                raise ValueError(f"Cannot overpaint egg, egg is already at {coverage}%")19                raise ValueError(f"Cannot overpaint egg, egg is already at {coverage}%")
2020
21            coverage_top = sum(percentage for _, percentage in self.top_coloring)21            coverage_top = sum(percentage for _, percentage in self.top_coloring)
22            if leftover_coverage_top := 50 - coverage_top:22            if leftover_coverage_top := 50 - coverage_top:
23                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))23                self.top_coloring.append((color, min(leftover_coverage_top, percentage)))
24            if coverage_top + percentage > 50:24            if coverage_top + percentage > 50:
25                self.bottom_coloring.append((color, percentage - leftover_coverage_top))25                self.bottom_coloring.append((color, percentage - leftover_coverage_top))
2626
27    def _calculate_hardness(self, side):27    def _calculate_hardness(self, side):
28        return sum(28        return sum(
29            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage29            (int(color[:2], base=16) + int(color[2:4], base=16) + int(color[4:], base=16) + 1) * percentage
30            for color, percentage in getattr(self, f"{side}_coloring")30            for color, percentage in getattr(self, f"{side}_coloring")
31        )31        )
3232
33    @property33    @property
34    def top_hardness(self):34    def top_hardness(self):
35        return self._calculate_hardness(EggSide.top)35        return self._calculate_hardness(EggSide.top)
3636
37    @property37    @property
38    def bottom_hardness(self):38    def bottom_hardness(self):
39        return self._calculate_hardness(EggSide.bottom)39        return self._calculate_hardness(EggSide.bottom)
4040
41    def break_side(self, side):41    def break_side(self, side):
42        self.broken_sides.append(side)42        self.broken_sides.append(side)
4343
44    def _collide_eggs(self, other, side):44    def _collide_eggs(self, other, side):
45        if side in self.broken_sides or side in other.broken_sides:45        if side in self.broken_sides or side in other.broken_sides:
46            raise TypeError("Cannot collide with broken sides")46            raise TypeError("Cannot collide with broken sides")
47        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):47        if getattr(self, f"{side}_hardness") > getattr(other, f"{side}_hardness"):
48            winner, loser = self, other48            winner, loser = self, other
49        else:49        else:
50            winner, loser = other, self50            winner, loser = other, self
51        loser.break_side(side)51        loser.break_side(side)
52        EggTournament.log_fight(self, other, side, winner)52        EggTournament.log_fight(self, other, side, winner)
53        return winner53        return winner
5454
55    def __mul__(self, other):55    def __mul__(self, other):
56        return self._collide_eggs(other, EggSide.top)56        return self._collide_eggs(other, EggSide.top)
5757
58    def __matmul__(self, other):58    def __matmul__(self, other):
59        return self._collide_eggs(other, EggSide.bottom)59        return self._collide_eggs(other, EggSide.bottom)
6060
6161
62class EggTournament:62class EggTournament:
63    instances = set()63    instances = set()
6464
65    def __init__(self):65    def __init__(self):
66        self.instances.add(self)66        self.instances.add(self)
67        self.registered_eggs = {}67        self.registered_eggs = {}
68        self.fight_history = {}68        self.fight_history = {}
6969
70    def register(self, egg, name):70    def register(self, egg, name):
71        if name in self.registered_eggs:71        if name in self.registered_eggs:
t72            raise ValueError("Egg with name the_monster has already been registered")t72            raise ValueError(f"Egg with name {name} has already been registered")
73        if not name.isidentifier():73        if not name.isidentifier():
74            raise ValueError("Invalid registration name")74            raise ValueError("Invalid registration name")
75        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):75        if any(egg in tournament.registered_eggs.values() for tournament in self.__class__.instances):
76            raise ValueError("An egg cannot be registered in multiple tournaments")76            raise ValueError("An egg cannot be registered in multiple tournaments")
77        self.registered_eggs[name] = egg77        self.registered_eggs[name] = egg
7878
79    @classmethod79    @classmethod
80    def log_fight(cls, first, second, side, winner):80    def log_fight(cls, first, second, side, winner):
81        for tournament in cls.instances:81        for tournament in cls.instances:
82            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():82            if first in tournament.registered_eggs.values() and second in tournament.registered_eggs.values():
83                tournament.fight_history[(first, second, side)] = winner83                tournament.fight_history[(first, second, side)] = winner
8484
85    @property85    @property
86    def egg_wins(self):86    def egg_wins(self):
87        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}87        return {egg: list(self.fight_history.values()).count(egg) for egg in self.registered_eggs.values()}
8888
89    @property89    @property
90    def ranking(self):90    def ranking(self):
91        return [91        return [
92            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}92            {egg for egg in self.registered_eggs.values() if self.egg_wins[egg] == wins_for_rank}
93            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)93            for wins_for_rank in sorted(set(self.egg_wins.values()), reverse=True)
94        ]94        ]
9595
96    def __getattr__(self, name):96    def __getattr__(self, name):
97        try:97        try:
98            egg = self.registered_eggs[name]98            egg = self.registered_eggs[name]
99        except KeyError:99        except KeyError:
100            raise AttributeError("Apologies, there is no such egg registered")100            raise AttributeError("Apologies, there is no such egg registered")
101        for rank, eggs in enumerate(self.ranking, start=1):101        for rank, eggs in enumerate(self.ranking, start=1):
102            if egg in eggs:102            if egg in eggs:
103                return {"position": rank, "victories": self.egg_wins[egg]}103                return {"position": rank, "victories": self.egg_wins[egg]}
104104
105    def __getitem__(self, key):105    def __getitem__(self, key):
106        if isinstance(key, slice):106        if isinstance(key, slice):
107            key = key.start, key.stop, key.step107            key = key.start, key.stop, key.step
108        try:108        try:
109            return self.fight_history[key]109            return self.fight_history[key]
110        except KeyError:110        except KeyError:
111            return self.fight_history[key[1], key[0], key[2]]111            return self.fight_history[key[1], key[0], key[2]]
112112
113    def __rmatmul__(self, position):113    def __rmatmul__(self, position):
114        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()114        return eggs_on_rank if len(eggs_on_rank := self.ranking[position - 1]) > 1 else eggs_on_rank.pop()
115115
116    def __contains__(self, value):116    def __contains__(self, value):
117        return value in self.registered_eggs.values()117        return value in self.registered_eggs.values()
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op