Домашни > Подаръци ще има за всички от сърце > Решения > Решението на Йоан Байчев

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

10 точки общо

20 успешни теста
0 неуспешни теста
Код

  1import re
  2
  3class Kid(type):
  4    _all_kids = {}
  5    _kid_indexes = {}
  6    _kid_list = []
  7    _next_index = 0
  8
  9    def __new__(mcs, name, bases, attrs):
 10        cls = super().__new__(mcs, name, bases, attrs)
 11
 12        if "__call__" not in attrs:
 13            has_call = any(hasattr(base, "__call__") for base in bases)
 14            if not has_call:
 15                raise NotImplementedError("Bruhhh")
 16
 17        def decorate_with_exception_handling(method):
 18            def wrapper(self, *args, **kwargs):
 19                kid_id = id(self)
 20                try:
 21                    return method(self, *args, **kwargs)
 22                except Exception:
 23                    santa = Santa()
 24                    kid_index = Kid._kid_indexes[kid_id]
 25                    santa._mark_naughty(kid_index)  
 26                    raise
 27            return wrapper
 28
 29        for attr_name, attr_value in attrs.items():
 30            if callable(attr_value) and not attr_name.startswith('_'):
 31                setattr(cls, attr_name, decorate_with_exception_handling(attr_value))
 32
 33        return cls
 34
 35    def __call__(cls, *args, **kwargs):
 36        obj = super().__call__(*args, **kwargs)
 37        kid_id = id(obj)
 38        Kid._all_kids[kid_id] = obj
 39        kid_index = Kid._next_index
 40        Kid._kid_list.append(obj)
 41        Kid._kid_indexes[kid_id] = kid_index
 42        Kid._next_index += 1
 43
 44        Santa()._register_kid(obj)  # Register the kid with Santa
 45
 46        return obj
 47
 48
 49class Santa:
 50    _instance = None
 51    AGE_LIMIT = 5  
 52
 53    def __new__(cls):
 54        if cls._instance is None:
 55            obj = super().__new__(cls)
 56            cls._instance = obj
 57        return cls._instance
 58
 59    def __init__(self):
 60        if not hasattr(self, 'initialized'):
 61            self.xmas_count = 0
 62            self.kid_birth_xmas = {}  # Tracks the Christmas count when the kid was registered
 63            self.last_requests = {}  # Stores the last gift requested by each kid
 64            self.requests_since_last_xmas = []  # List of all requests since last Christmas
 65            self.kid_flags_mask = 0  # Bitmask to track kid statuses
 66            self.initialized = True
 67
 68    def _mark_naughty(self, kid_index):
 69        self.kid_flags_mask |= (1 << (kid_index * 3))  
 70
 71    def _is_naughty(self, kid_index):
 72        return (self.kid_flags_mask & (1 << (kid_index * 3))) != 0
 73
 74    def _mark_requested(self, kid_index):
 75        self.kid_flags_mask |= (1 << (kid_index * 3 + 1))  
 76
 77    def _is_requested(self, kid_index):
 78        return (self.kid_flags_mask & (1 << (kid_index * 3 + 1))) != 0
 79
 80    def _set_age_flag(self, kid_index, is_over_age):
 81        if is_over_age:
 82            self.kid_flags_mask &= ~(1 << (kid_index * 3 + 2))  
 83        else:
 84            self.kid_flags_mask |= (1 << (kid_index * 3 + 2))  
 85
 86    def _is_over_age(self, kid_index):
 87        return not ((self.kid_flags_mask & (1 << (kid_index * 3 + 2))) != 0)
 88
 89    def _register_kid(self, kid):
 90        kid_id = id(kid)
 91        if kid_id not in self.kid_birth_xmas:
 92            # Register the kid's birth Christmas
 93            self.kid_birth_xmas[kid_id] = self.xmas_count
 94            kid_index = Kid._kid_indexes[kid_id]
 95            self._set_age_flag(kid_index, is_over_age=False)
 96
 97    def __call__(self, kid, input_string):
 98        gift = self._get_gift(input_string)
 99        kid_id = id(kid)
100        if kid_id not in self.kid_birth_xmas:
101            self.kid_birth_xmas[kid_id] = self.xmas_count  # Register the kid if not already registered
102
103        self.requests_since_last_xmas.append((kid_id, gift))  # Add the request to the lists
104        self.last_requests[kid_id] = gift
105        kid_index = Kid._kid_indexes[kid_id]
106        self._mark_requested(kid_index) 
107
108    def __matmul__(self, input_string):
109        gift = self._get_gift(input_string)
110        kid_id = self._get_kid_id_from_letter(input_string)
111        kid = Kid._all_kids[kid_id]
112        if kid_id not in self.kid_birth_xmas:
113            self.kid_birth_xmas[kid_id] = self.xmas_count  # Register the kid if not already registered
114
115        self.requests_since_last_xmas.append((kid_id, gift))  # Add the request to the lists
116        self.last_requests[kid_id] = gift
117        kid_index = Kid._kid_indexes[kid_id]
118        self._mark_requested(kid_index)  
119
120        return self
121
122    def _get_gift(self, input_string):
123        pattern = r'(["\'])([A-Za-z0-9 ]+)\1'
124        match = re.search(pattern, input_string)
125        if not match:
126            raise ValueError("Error")
127        return match.group(2)
128
129    def _get_kid_id_from_letter(self, input_string):
130        match = re.search(r'^\s*(\d+)\s*$', input_string, re.MULTILINE)
131        if match:
132            kid_id = int(match.group(1))
133            if kid_id in Kid._all_kids:
134                return kid_id
135        raise ValueError("Error")
136
137    def __iter__(self):
138        return iter(self.last_requests.values())
139
140    def xmas(self):
141        if not self.requests_since_last_xmas:
142            self._reset()
143            return
144
145        chosen_most_wanted = self._most_wanted_gift()
146        self._deliver(chosen_most_wanted)
147        self._reset()
148
149    def _most_wanted_gift(self):
150        if not self.last_requests:
151            return None
152
153        gift_counts = {}
154        for gift in self.last_requests.values():
155            gift_counts[gift] = gift_counts.get(gift, 0) + 1
156
157        if not gift_counts:
158            return None
159
160        max_count = max(gift_counts.values())
161        most_wanted = [g for g, c in gift_counts.items() if c == max_count]
162        return most_wanted[0] if most_wanted else None 
163
164    def _deliver(self, chosen_most_wanted):
165        for kid_id, kid in Kid._all_kids.items():
166            birth = self.kid_birth_xmas.get(kid_id, self.xmas_count)
167            age = self.xmas_count - birth
168            kid_index = Kid._kid_indexes[kid_id]
169
170            if age >= self.AGE_LIMIT:
171                self._set_age_flag(kid_index, is_over_age=True)  # Mark the kid as over age and skip delivery
172                continue
173            else:
174                self._set_age_flag(kid_index, is_over_age=False)  # Reset the age flag if kid is not over age
175
176            naughty = self._is_naughty(kid_index)  
177
178            if kid_id in self.last_requests:
179                gift = self.last_requests[kid_id]
180            else:
181                gift = chosen_most_wanted
182                if gift is None:
183                    continue
184
185            if naughty:
186                gift = "coal"
187
188            kid(gift)  
189
190    def _reset(self):
191        # Reset the state for the next Christmas
192        self.xmas_count += 1
193        self.requests_since_last_xmas.clear()
194        self.last_requests.clear()
195        self.kid_flags_mask = 0  

....................
----------------------------------------------------------------------
Ran 20 tests in 0.084s

OK

Дискусия
Йоан Байчев
19.12.2024 02:04

Гледах да оправя забележките, като също добавих още някои неща и промених побитовата манипулация! - Промених итератора така, че ако дете прави нова заявка, старата да се презапише. - Махнах "strip()", за да оставя празните пространства(ако ги има) ограждащи дадено желание. - Добавих "Santa().register_kid(obj)" за централно управление на децата, което улеснява управлението на техните статуси. - Добавих нов регекс за числата, който извлеча само числото от входния низ, като игнорира всякакви ограждащи празни пространства. Вече за всяко дете отговарят точно три последователни бита, като те се съхраняват в единна битова маска - kid_flags_mask. - Десен бит: той отговаря за това дали детето е послушно, ако той е вдигнат(1) значи детето не е послушно. - Среден бит: той отговаря за това дали детето е направило заявка за подарък през текущата Коледа, отново ако е вдигнат(1) заявка има. - Ляв бит: той отговаря за това дали детето е над възрастовата граница, вдигнат(1) бит ни казва, че детето е "голямо". Съответно съм добавил нужните методи, които сетват и проверяват дадения бит за даденото състояние. Важно е да се каже, че "индексът" на детето "сочи" към десния бит и индексите на всеки следващи деца се прескачат през 3 бита! Пример 0b010000111: - Първо дете(индекс 0): 111, непослушно, поискало подарък, над възрастовия лимит. - Второ дете(индекс 1): 000, послушно, непоискало подарък, под възрастовия лимит. - Трето дете(индекс 2): 010, послушно, поискало подарък, под възрастовия лимит.
Йоан Байчев
17.12.2024 02:04

1. Използвам побитови операции за съхранение и проверка на определени състояния на децата — дали са поискали подарък и дали са непослушни. Така вместо да имаме отделни структури (например речник kid_naughty = {kid_id: True/False}), използвам един единствен int, в който всеки бит отговаря на конкретно дете. Ако kid_index е индексът на детето, то битът на позиция kid_index в един битов регистър може да показва дали детето е непослушно (1) или не (0). - naughty = (kid_naughty_mask & (1 << kid_index)) != 0 с това проверявам дали битът е вдигнат тоест, дали конкретното дете е непослушно. - kid_naughty_mask |= (1 << kid_index) с това пък задавам на конкретното дете да е непослушно, тъй като логическо "или" с истина(1) е еднозначно определено и винаги връща истина(1) - kid_naughty_mask = 0, kids_requested_mask = 0 това моментално изчиства цялата информация, без да обхождаме речници, списъци или други структури. Реално един битов регистър може да отрази статуса на десетки или стотици деца само в едно число. 2. Относно патерна - първият ми клас е за видовете кавички(единични или двойни) като е ограден като група, за да мога да я преизполвам при затварянето, вторият ми клас е очевиден(валидиращ малки, големи букви(латински), цифри и " " за разделение между тях), отново ограждам като група за уловя текста на желанието. Патернът не поддържа използването на кавички вътре в желанията, не съм ги ескейпвал, но май не е и нужно? Последно "\1" се обръща към групата с кавичките, като подсигурява, че с какъвто вид кавички сме почнали с такъв трябва да приключим. 3. Относно __matmul__, видях че поддържа оператора "@" с цел матрично умножение, но в случая ни върши работа за целите на домашното. 4. Относно decorate_with_exception_handling - декорира методите на наследниците, като улавя изключения, маркира детето като непослушно(при настъпило изключение) и предава изключението нататък, за да не може да бъде пипано.
История

f1import ref1import re
n2import randomn
3 
4ALL_KIDS = {}
5KID_INDEXES = {}
6KID_LIST = []
72
8class Kid(type):3class Kid(type):
nn4    _all_kids = {}
5    _kid_indexes = {}
6    _kid_list = []
7    _next_index = 0
8 
9    def __new__(mcs, name, bases, attrs):9    def __new__(mcs, name, bases, attrs):
n10        cls = super().__new__(mcs, name, bases, dict(attrs))n10        cls = super().__new__(mcs, name, bases, attrs)
1111
12        if "__call__" not in attrs:12        if "__call__" not in attrs:
13            has_call = any(hasattr(base, "__call__") for base in bases)13            has_call = any(hasattr(base, "__call__") for base in bases)
14            if not has_call:14            if not has_call:
15                raise NotImplementedError("Bruhhh")15                raise NotImplementedError("Bruhhh")
1616
17        def decorate_with_exception_handling(method):17        def decorate_with_exception_handling(method):
18            def wrapper(self, *args, **kwargs):18            def wrapper(self, *args, **kwargs):
19                kid_id = id(self)19                kid_id = id(self)
20                try:20                try:
21                    return method(self, *args, **kwargs)21                    return method(self, *args, **kwargs)
22                except Exception:22                except Exception:
n23                    if Santa._instance is not None:n
24                        santa = Santa._instance23                    santa = Santa()
25                        kid_index = KID_INDEXES[kid_id]24                    kid_index = Kid._kid_indexes[kid_id]
26                        santa.kid_naughty_mask |= (1 << kid_index)  # Marking the kid as naughty25                    santa._mark_naughty(kid_index)  
27                    raise26                    raise
28            return wrapper27            return wrapper
2928
30        for attr_name, attr_value in attrs.items():29        for attr_name, attr_value in attrs.items():
31            if callable(attr_value) and not attr_name.startswith('_'):30            if callable(attr_value) and not attr_name.startswith('_'):
32                setattr(cls, attr_name, decorate_with_exception_handling(attr_value))31                setattr(cls, attr_name, decorate_with_exception_handling(attr_value))
3332
34        return cls33        return cls
3534
36    def __call__(cls, *args, **kwargs):35    def __call__(cls, *args, **kwargs):
n37        # Register a new kid instance globallyn
38        obj = super().__call__(*args, **kwargs)36        obj = super().__call__(*args, **kwargs)
39        kid_id = id(obj)37        kid_id = id(obj)
n40        ALL_KIDS[kid_id] = objn38        Kid._all_kids[kid_id] = obj
41        kid_index = len(KID_LIST)39        kid_index = Kid._next_index
42        KID_LIST.append(obj)40        Kid._kid_list.append(obj)
43        KID_INDEXES[kid_id] = kid_index41        Kid._kid_indexes[kid_id] = kid_index
42        Kid._next_index += 1
43 
44        Santa()._register_kid(obj)  # Register the kid with Santa
45 
44        return obj46        return obj
4547
4648
47class Santa:49class Santa:
48    _instance = None50    _instance = None
nn51    AGE_LIMIT = 5  
4952
50    def __new__(cls):53    def __new__(cls):
51        if cls._instance is None:54        if cls._instance is None:
52            obj = super().__new__(cls)55            obj = super().__new__(cls)
53            cls._instance = obj56            cls._instance = obj
n54            obj.xmas_count = 0n
55            obj.kid_birth_xmas = {}  # kid_id - xmas_count when the kid was created
56            obj.last_requests = {}  # kid_id - last requested gift this year
57            obj.requests_since_last_xmas = []
58 
59            obj.kid_naughty_mask = 0  # Bitmask for naughty kids
60            obj.kids_requested_mask = 0  # Bitmask for kids who requested a gift this year
61 
62        return cls._instance57        return cls._instance
6358
64    def __init__(self):59    def __init__(self):
n65        passn60        if not hasattr(self, 'initialized'):
61            self.xmas_count = 0
62            self.kid_birth_xmas = {}  # Tracks the Christmas count when the kid was registered
63            self.last_requests = {}  # Stores the last gift requested by each kid
64            self.requests_since_last_xmas = []  # List of all requests since last Christmas
65            self.kid_flags_mask = 0  # Bitmask to track kid statuses
66            self.initialized = True
67 
68    def _mark_naughty(self, kid_index):
69        self.kid_flags_mask |= (1 << (kid_index * 3))  
70 
71    def _is_naughty(self, kid_index):
72        return (self.kid_flags_mask & (1 << (kid_index * 3))) != 0
73 
74    def _mark_requested(self, kid_index):
75        self.kid_flags_mask |= (1 << (kid_index * 3 + 1))  
76 
77    def _is_requested(self, kid_index):
78        return (self.kid_flags_mask & (1 << (kid_index * 3 + 1))) != 0
79 
80    def _set_age_flag(self, kid_index, is_over_age):
81        if is_over_age:
82            self.kid_flags_mask &= ~(1 << (kid_index * 3 + 2))  
83        else:
84            self.kid_flags_mask |= (1 << (kid_index * 3 + 2))  
85 
86    def _is_over_age(self, kid_index):
87        return not ((self.kid_flags_mask & (1 << (kid_index * 3 + 2))) != 0)
88 
89    def _register_kid(self, kid):
90        kid_id = id(kid)
91        if kid_id not in self.kid_birth_xmas:
92            # Register the kid's birth Christmas
93            self.kid_birth_xmas[kid_id] = self.xmas_count
94            kid_index = Kid._kid_indexes[kid_id]
95            self._set_age_flag(kid_index, is_over_age=False)
6696
67    def __call__(self, kid, input_string):97    def __call__(self, kid, input_string):
68        gift = self._get_gift(input_string)98        gift = self._get_gift(input_string)
69        kid_id = id(kid)99        kid_id = id(kid)
70        if kid_id not in self.kid_birth_xmas:100        if kid_id not in self.kid_birth_xmas:
n71            self.kid_birth_xmas[kid_id] = self.xmas_countn101            self.kid_birth_xmas[kid_id] = self.xmas_count  # Register the kid if not already registered
72102
n73        self.requests_since_last_xmas.append((kid_id, gift))n103        self.requests_since_last_xmas.append((kid_id, gift))  # Add the request to the lists
74        self.last_requests[kid_id] = gift104        self.last_requests[kid_id] = gift
n75        kid_index = KID_INDEXES[kid_id]n105        kid_index = Kid._kid_indexes[kid_id]
76        # Mark this kid as having requested a gift this year
77        self.kids_requested_mask |= (1 << kid_index)106        self._mark_requested(kid_index) 
78107
79    def __matmul__(self, input_string):108    def __matmul__(self, input_string):
80        gift = self._get_gift(input_string)109        gift = self._get_gift(input_string)
81        kid_id = self._get_kid_id_from_letter(input_string)110        kid_id = self._get_kid_id_from_letter(input_string)
n82        kid = ALL_KIDS[kid_id]n111        kid = Kid._all_kids[kid_id]
83        if kid_id not in self.kid_birth_xmas:112        if kid_id not in self.kid_birth_xmas:
n84            self.kid_birth_xmas[kid_id] = self.xmas_countn113            self.kid_birth_xmas[kid_id] = self.xmas_count  # Register the kid if not already registered
85114
n86        self.requests_since_last_xmas.append((kid_id, gift))n115        self.requests_since_last_xmas.append((kid_id, gift))  # Add the request to the lists
87        self.last_requests[kid_id] = gift116        self.last_requests[kid_id] = gift
n88        kid_index = KID_INDEXES[kid_id]n117        kid_index = Kid._kid_indexes[kid_id]
89        # Mark this kid as having requested a gift this year
90        self.kids_requested_mask |= (1 << kid_index)118        self._mark_requested(kid_index)  
91119
92        return self120        return self
93121
94    def _get_gift(self, input_string):122    def _get_gift(self, input_string):
95        pattern = r'(["\'])([A-Za-z0-9 ]+)\1'123        pattern = r'(["\'])([A-Za-z0-9 ]+)\1'
96        match = re.search(pattern, input_string)124        match = re.search(pattern, input_string)
97        if not match:125        if not match:
98            raise ValueError("Error")126            raise ValueError("Error")
n99        return match.group(2).strip() # Removing this kind of symbols : "\t", "\n" ...n127        return match.group(2)
100128
101    def _get_kid_id_from_letter(self, input_string):129    def _get_kid_id_from_letter(self, input_string):
n102        for line in input_string.split('\n'):n130        match = re.search(r'^\s*(\d+)\s*$', input_string, re.MULTILINE)
103            line_stripped = line.strip()131        if match:
104            if self._is_all_digits(line_stripped):132            kid_id = int(match.group(1))
105                kid_id = int(line_stripped)133            if kid_id in Kid._all_kids:
106                if kid_id in ALL_KIDS:
107                    return kid_id134                return kid_id
108                else:
109                    raise ValueError("Error")
110        raise ValueError("Error")135        raise ValueError("Error")
111136
n112    @staticmethodn
113    def _is_all_digits(input_string):
114        if not input_string:
115            return False  
116        for current in input_string:
117            if not ("0" <= current <= "9"):
118                return False
119        return True
120 
121    def __iter__(self):137    def __iter__(self):
n122        return (gift for (unused, gift) in self.requests_since_last_xmas)n138        return iter(self.last_requests.values())
123139
124    def xmas(self):140    def xmas(self):
125        if not self.requests_since_last_xmas:141        if not self.requests_since_last_xmas:
n126            self._no_requests_this_year()n142            self._reset()
127            return143            return
128144
129        chosen_most_wanted = self._most_wanted_gift()145        chosen_most_wanted = self._most_wanted_gift()
130        self._deliver(chosen_most_wanted)146        self._deliver(chosen_most_wanted)
131        self._reset()147        self._reset()
n132 n
133    def _no_requests_this_year(self):
134        self.xmas_count += 1
135        self.kid_naughty_mask = 0
136        self.last_requests.clear()
137        self.kids_requested_mask = 0
138148
139    def _most_wanted_gift(self):149    def _most_wanted_gift(self):
140        if not self.last_requests:150        if not self.last_requests:
141            return None151            return None
142152
143        gift_counts = {}153        gift_counts = {}
144        for gift in self.last_requests.values():154        for gift in self.last_requests.values():
145            gift_counts[gift] = gift_counts.get(gift, 0) + 1155            gift_counts[gift] = gift_counts.get(gift, 0) + 1
146156
147        if not gift_counts:157        if not gift_counts:
148            return None158            return None
149159
150        max_count = max(gift_counts.values())160        max_count = max(gift_counts.values())
151        most_wanted = [g for g, c in gift_counts.items() if c == max_count]161        most_wanted = [g for g, c in gift_counts.items() if c == max_count]
n152        return random.choice(most_wanted) if most_wanted else Nonen162        return most_wanted[0] if most_wanted else None 
153163
154    def _deliver(self, chosen_most_wanted):164    def _deliver(self, chosen_most_wanted):
n155        for kid_id, kid in ALL_KIDS.items():n165        for kid_id, kid in Kid._all_kids.items():
156            birth = self.kid_birth_xmas.get(kid_id, self.xmas_count)166            birth = self.kid_birth_xmas.get(kid_id, self.xmas_count)
157            age = self.xmas_count - birth167            age = self.xmas_count - birth
nn168            kid_index = Kid._kid_indexes[kid_id]
169 
158            if age >= 6:170            if age >= self.AGE_LIMIT:
171                self._set_age_flag(kid_index, is_over_age=True)  # Mark the kid as over age and skip delivery
159                continue172                continue
n160            kid_index = KID_INDEXES[kid_id]n173            else:
161            naughty = (self.kid_naughty_mask & (1 << kid_index)) != 0  # Checking if the child is naughty174                self._set_age_flag(kid_index, is_over_age=False)  # Reset the age flag if kid is not over age
175 
176            naughty = self._is_naughty(kid_index)  
162177
163            if kid_id in self.last_requests:178            if kid_id in self.last_requests:
164                gift = self.last_requests[kid_id]179                gift = self.last_requests[kid_id]
n165                if naughty:n
166                    gift = "coal"
167            else:180            else:
168                gift = chosen_most_wanted181                gift = chosen_most_wanted
169                if gift is None:182                if gift is None:
170                    continue183                    continue
n171                if naughty:n
172                    gift = "coal"
173184
n174            kid(gift)  # Call the kid with the chosen giftn185            if naughty:
186                gift = "coal"
187 
188            kid(gift)  
175189
176    def _reset(self):190    def _reset(self):
n177        # Reset state for the next yearn191        # Reset the state for the next Christmas
178        self.xmas_count += 1192        self.xmas_count += 1
179        self.requests_since_last_xmas.clear()193        self.requests_since_last_xmas.clear()
180        self.last_requests.clear()194        self.last_requests.clear()
t181        self.kid_naughty_mask = 0t195        self.kid_flags_mask = 0  
182        self.kids_requested_mask = 0
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op