1import random
2import re
3from collections import defaultdict
4
5class Singleton(type):
6 _instances = {}
7
8 def __call__(mcs, *args, **kwargs):
9 if mcs not in mcs._instances:
10 mcs._instances[mcs] = super(Singleton, mcs).__call__(*args, **kwargs)
11 return mcs._instances[mcs]
12
13
14class Kid(type):
15 instances = {} # id -> {child: self, xmas_count: 0, bad: False}
16
17 def __new__(mcs, name, bases, class_dict):
18 if "__call__" not in class_dict:
19 raise NotImplementedError(f"The class {name} must define a __call__ method.")
20
21 cls = super().__new__(mcs, name, bases, class_dict)
22
23 original_init = cls.__init__
24
25 def init_wrapper(self, *args, **kwargs):
26 original_init(self, *args, **kwargs)
27 Kid.instances[id(self)] = {'child': self, 'xmas_count': 0, 'bad': False}
28
29 cls.__init__ = init_wrapper
30
31 for attr_name, attr_value in class_dict.items():
32 if callable(attr_value) and not attr_name.startswith("__") and attr_name != "__call__":
33 def method_wrapper(self, *args, **kwargs):
34 try:
35 return attr_value(self, *args, **kwargs)
36 except Exception:
37 Kid.instances[id(self)]['bad'] = True
38 return None
39
40 setattr(cls, attr_name, method_wrapper)
41
42 return cls
43
44
45class Santa(metaclass=Singleton):
46 def __init__(self):
47 self.wishes = {}
48 self.wishes_ranking = defaultdict(int)
49 self.were_there_wished = False
50
51 def __call__(self, child, wish):
52 if not isinstance(type(child), Kid):
53 raise TypeError("Only instances of classes with Kid as metaclass are allowed.")
54 if not isinstance(wish, str):
55 raise ValueError("The wish must be a string.")
56
57 extracted_wish_text = self._extract_wish(wish)
58
59 if not extracted_wish_text:
60 raise ValueError("The message must contain a valid wish enclosed in quotes.")
61
62 self.wishes[id(child)] = extracted_wish_text
63 self.wishes_ranking[extracted_wish_text] += 1
64 self.were_there_wished = True
65
66 def __matmul__(self, letter):
67 if not isinstance(letter, str):
68 raise ValueError("The letter must be a string.")
69
70 extracted_wish_text = self._extract_wish(letter)
71
72 if not extracted_wish_text:
73 raise ValueError("The letter must contain a valid wish enclosed in quotes.")
74
75 signature_regex = r'\n\s*(\d+)\s*$'
76 signature_match = re.search(signature_regex, letter)
77
78 if not signature_match:
79 raise ValueError("The letter must contain a valid numeric signature.")
80
81 signature = int(signature_match.group(1))
82 self.wishes[signature] = extracted_wish_text
83 self.wishes_ranking[extracted_wish_text] += 1
84 self.were_there_wished = True
85
86 def _extract_wish(self, text):
87 wish_regex = r'(["\'])([a-zA-Z0-9 ]+)\1'
88 extracted_wish = re.search(wish_regex, text)
89
90 if not extracted_wish:
91 return None
92
93 return extracted_wish.group(2) or extracted_wish.group(3)
94
95 def __iter__(self):
96 for wish in self.wishes.values():
97 yield wish
98
99 def _get_most_wished_wish(self):
100 max_value = max(self.wishes_ranking.values())
101 max_wished = [key for key, value in self.wishes_ranking.items() if value == max_value]
102 return random.choice(max_wished)
103
104 def xmas(self):
105 if not self.were_there_wished:
106 for kid_info in Kid.instances.values():
107 kid_info['xmas_count'] += 1
108 return
109
110 # wish for the rest of the children
111 random_wish = self._get_most_wished_wish()
112
113 # all children that wished
114 for kid_id, wish in (self.wishes | {kid_id : random_wish for kid_id in (Kid.instances.keys() - self.wishes.keys())}).items():
115 kid_info = Kid.instances[kid_id]
116
117 if kid_info['xmas_count'] >= 5:
118 continue
119
120 if kid_info['bad']:
121 kid_info['child']('coal')
122 print(f"{kid_info['child']} -> coal")
123 else:
124 kid_info['child'](wish)
125 print(f"{kid_info['child']} -> {wish}")
126
127 kid_info['xmas_count'] += 1
128 kid_info['bad'] = False
129
130 self.wishes.clear()
131 self.wishes_ranking = defaultdict(int)
132 self.were_there_wished = False
......E......F..F
Stdout:
<solution.KidClass1 object at 0x74792e6e2060> -> coal
F..
======================================================================
ERROR: test_present_matching (test.TestSanta.test_present_matching)
Test matching signature in the letter.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 87, in test_present_matching
self.santa @ f"""
File "/tmp/solution.py", line 79, in __matmul__
raise ValueError("The letter must contain a valid numeric signature.")
ValueError: The letter must contain a valid numeric signature.
======================================================================
FAIL: test_xmass_naughty (test.TestSanta.test_xmass_naughty)
Test a Christmas with naughty kids.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 222, in test_xmass_naughty
with self.assertRaises(ZeroDivisionError):
AssertionError: ZeroDivisionError not raised
======================================================================
FAIL: test_xmass_private_with_error (test.TestSanta.test_xmass_private_with_error)
Test a Christmas with not-so-naughty kids.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 240, in test_xmass_private_with_error
self.assertEqual(kid1.SECRET_PRESENT, 'sirenie')
AssertionError: 'coal' != 'sirenie'
- coal
+ sirenie
Stdout:
<solution.KidClass1 object at 0x74792e6e2060> -> coal
======================================================================
FAIL: test_xmass_public_with_no_error (test.TestSanta.test_xmass_public_with_no_error)
Test a Christmas with not-so-naughty kids.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 246, in test_xmass_public_with_no_error
self.assertEqual(kid1.public_without_error(), 42)
AssertionError: None != 42
----------------------------------------------------------------------
Ran 20 tests in 0.027s
FAILED (failures=3, errors=1)
19.12.2024 16:43
19.12.2024 16:44
19.12.2024 16:46