Домашни > Хелоуин в Припят


Хелоуин в Припят
Краен срок: 07.11.2023 18:00
Точки: 10

### Увод Децата в Припят ще обикалят за Хелоуин, но домакините са напазарували сладкиши с известно съдържание на обогатен уран :radioactive:. Когато достатъчно количество уран се събере в кошницата на някое дете, се стига до ["Критична маса"](https://bg.wikipedia.org/wiki/%D0%9A%D1%80%D0%B8%D1%82%D0%B8%D1%87%D0%BD%D0%B0_%D0%BC%D0%B0%D1%81%D0%B0) и детето получава летална доза облъчване. ![Not great, not terrible](/media/resources/pripyat.png) Вашата задача е да предвидите събитията и да помогнете на децата, като разберете кое ще загине първо. ### Задачата Напишете следните класове. ##### Клас `Candy` Клас, който представлява лакомство за раздаване от домакини на деца. - Аргументи при инициализация: - `mass` - масата в грамове /*теглото*/ (да, наясно сме, че маса и тегло са различни физични понятия, но да е ясно какво е това) на дадено лакомство. - `uranium` - число между 0 и 1, което показва процентно количество уран в лакомството; 0 значи, че няма уран, а 1 - има 100% съдържание на уран. - Публични методи, които класът имплементира: - `get_uranium_quantity` - връща масата на уран за лакомството в грамове; Например, десерт с маса 5гр и ураново съдържание от 0.5 ще върне 2.5. - `get_mass` - връща масата на лакомството така, както е получена при инициализиране на обекта. ##### Клас `Person` Клас, който представлява участник в събитието. - Аргументи при инициализация: - `position` - `tuple` от `int`-ове, които показват позицията на участника в двумерна координатна система. Например `(1, 2)` значи, че персонажът се намира на x=1, y=2. - Публични методи, които класът имплементира: - `get_position` - връща `tuple`-ът, който показва позицията на персонажа. - `set_position` - получава `tuple` от `int`-ове, които представляват нова позиция, и презаписва позицията на персонажа с нея. ##### Клас `Kid` Клас, който представлява дете, което обикаля по къщите за лакомства. Помислете как ще се възползвате от класа `Person`, който вече е дефиниран. - Аргументи при инициализация: - `position` - `tuple` от `int`-ове, които показват началната позицията на детето в двумерна координатна система. - `initiative` - `int`, който дефинира "инициативност" на детето (служи за разрешаване на това, кое дете е първо при колизии). - Публични методи, които класът имплементира: - `get_initiative` - връща `int`, който представлява инициативността на детето. - `add_candy` - получава `Candy` инстанция и я слага в кошницата си. Къде и как ще пазите информация за това какви лакомства има едно дете, зависи от вас. - `is_critical` - връща булева стойност в зависимост от това дали количеството уран в кошницата на детето надвишава 20гр. ##### Клас `Host` Клас, който представлява домакин, който си седи вкъщи и чака деца, за да им даде лакомства. Помислете как ще се възползвате от класа `Person`, който вече е дефиниран. - Аргументи при инициализация: - `position` - `tuple` от `int`-ове, които показват позицията на домакина в двумерна координатна система. - `candies` - `list` от `tuple`-и с по два елемента - един `int` и един `float`. Всеки `tuple` представлява аргументите, нужни за създаване на `Candy`. `int`-ът на първа позиция е масата на лакомството, а `float`-ът на втора позиция е урановото съдържание. Информацията се използва, за да напълни кошницата на домакина с лакомства, които да се раздават на децата. - Публични методи, които класът имплементира: - `remove_candy` - получава като вход функция, която определя кое лакомство от тип `Candy` да се извади от кошницата на домакина и да се върне от метода (за да се даде по-късно на дете). Функцията, която се подава на входа, ще приема списък (`list`, `set`, `tuple`...) от лакомства и ще връща едно лакомство от списъка. Примерна функция би била такава, която от списък с лакомства връща това лакомство, което има най-голяма маса. Ако домакинът няма лакомства, методът `remove_candy` трябва да връща `None` без да извиква функцията, която е получил на вход. ##### Клас `FluxCapacitor` Клас, който представлява симулация на събитията, които ще се развият през нощта на Хелоуин в Припят. В началото всяко дете отива при най-близкия си домакин (мигновено), след което всички домакини обслужват пристигналите при тях деца с лакомства (отново мигновено). Ако след тези мигновени събитие има достатъчно количество уран в нечия кошница - децата, които притежават тези кошници, падат в жертва. Ако не, след раздаване на лакомства, вечерта продължава като всяко дете отива при следващия най-близък до него домакин, който все още не е навестило, и събитията се повтарят. - Аргументи при инициализация: - `participants` - `set` от `Kid` и `Host` инстанции без определен ред и без разделение между двата вида участници. Всички участници на събитието се подават през този единствен `set`. - Публични методи, които класът имплементира: - `get_victim` - връща `set` от инстанции на `Kid`, които показват първите деца, които ще съберат достатъчно количество уран в лакомствата си, че да се получи критична маса и да бъдат облъчени. При първите жертви "играта" приключва и обикалянето по къщи спира. В случай, че няма жертви, функцията връща `None`. ### Уговорки - Всяко дете минава през даден домакин само по веднъж. - Ако всички деца обходят всички домакини и не се достигне до критична маса, всички си тръгват живи и здрави и `FluxCapacitor` връща `None`. - Всяко дете решава кой да е следващият домакин, който да посети, чрез разстоянието, на което се намира от него. Винаги отива в най-близкия домакин, който все още не е посетило. - В случай, че дете има няколко домакина в близост до себе си, които са на едно и също разстояние, първо посещава този домакин, която има по-ниска стойност на *x* координатата си. Ако и тя е еднаква - този, с по-ниска стойност на *y* координатата си. - Ситуация, в която началната позиция на които и да било двама участници (деца или домакини) съвпада, се счита за невалидна. Няма да тестваме с такива. - Когато домакинът избира кое лакомство да даде на дете, винаги трябва да даде това лакомство от кошницата си, което има най-голяма маса (не най-голяма уранова маса, а чиста маса). - Ситуация, в която има лакомства с еднаква маса се счита за невалидна и няма да тестваме с такива. - Когато при даден домакин едновременно пристигнат две или повече деца, първо се обслужват тези с по-висока инициатива. - Ситуация, в която има деца с еднаква инициатива се счита за невалидна и няма да тестваме с такива, за да избегнем случайния принцип. - Ако дете стигне при домакин, който вече няма лакомства, му прави номер и продължава без да получи лакомство. Нямаме имплементация на номера, просто приемете, че минава транзит и продължава без лакомство. ### Disclaimer - Да, караме ви да пишете гетъри на класовете си, което реално не е нужно и дори би се счело за грешна практика. Гетърите са дефинирани само за да можем да изтестваме класовете би без изрично да казваме как да държите данните си в тях. ### Примерен вход ``` candy = Candy(20, 0.3) person = Person((1, 2)) kid = Kid((0, 0), 123) host = Host((3, 4), [(1, 1.0), (2, 0.5)]) flux_capacitor = FluxCapacitor({kid, host}) ```
 1import unittest
 2
 3from solution import *
 4
 5
 6class SanityTest(unittest.TestCase):
 7    """Sanity tests for Halloween in Pripyat."""
 8
 9    def test_candy(self):
10        """Test Candy class."""
11        self.assertIsInstance(Candy(1, 1.0), Candy)
12
13    def test_person(self):
14        """Test Person class."""
15        self.assertIsInstance(Person((1, 1)), Person)
16
17    def test_kid(self):
18        """Test Kid class."""
19        self.assertIsInstance(Kid((1, 1), 1), Kid)
20
21    def test_host(self):
22        """Test Host class."""
23        self.assertIsInstance(Host((1, 1), [(1, 1.0)]), Host)
24
25    def test_flux_capacitor(self):
26        """Test FluxCapacitor class."""
27        kid = Kid((1, 1), 1)
28        host = Host((1, 1), [(1, 1.0)])
29        self.assertIsInstance(FluxCapacitor({kid, host}), FluxCapacitor)
30
31
32if __name__ == '__main__':
33    unittest.main()
  1import unittest
  2
  3import timeout_decorator
  4
  5from solution import *
  6
  7
  8class CandyTest(unittest.TestCase):
  9    """Tests for the Candy class."""
 10
 11    def test_basic_usage(self):
 12        """Test basic usage of Candy class."""
 13        candy = Candy(5, 0.5)
 14        self.assertEqual(candy.get_mass(), 5)
 15        self.assertEqual(candy.get_uranium_quantity(), 2.5)
 16
 17
 18class PersonTest(unittest.TestCase):
 19    """Tests for the Person class."""
 20
 21    def test_basic_usage(self):
 22        """Test basic usage of Person class."""
 23        person = Person((0, 0))
 24        self.assertEqual(person.get_position(), (0, 0))
 25        person.set_position((2, 2))
 26        self.assertEqual(person.get_position(), (2, 2))
 27
 28
 29class KidTest(unittest.TestCase):
 30    """Tests for the Kid class."""
 31
 32    def test_inheritance(self):
 33        """Ensure Kid inherits from Person."""
 34        self.assertTrue(issubclass(Kid, Person))
 35
 36    def test_basic_usage(self):
 37        """Test basic usage of Kid class."""
 38        kid = Kid((0, 0), 666)
 39        self.assertEqual(kid.get_position(), (0, 0))
 40        self.assertEqual(kid.get_initiative(), 666)
 41
 42    def test_candies(self):
 43        """Test basic usage of candies in the Kid class."""
 44        kid = Kid((0, 0), 666)
 45        self.assertFalse(kid.is_critical())
 46        candy = Candy(20, 1)
 47        kid.add_candy(candy)
 48        self.assertFalse(kid.is_critical())
 49        candy = Candy(0.1, 1)
 50        kid.add_candy(candy)
 51        self.assertTrue(kid.is_critical())
 52
 53
 54class HostTest(unittest.TestCase):
 55    """Tests for the Host class."""
 56
 57    def test_inheritance(self):
 58        """Ensure Host inherits from Person."""
 59        self.assertTrue(issubclass(Host, Person))
 60
 61    def test_basic_usage(self):
 62        """Test basic usage of Host class."""
 63        compare_fun = lambda candies: min(candies, key=lambda candy: candy.get_mass())
 64        host = Host((0, 0), [(456, 1.0), (785, 0.5)])
 65        candy = host.remove_candy(compare_fun)
 66        self.assertEqual(candy.get_mass(), 456)
 67        candy = host.remove_candy(compare_fun)
 68        self.assertEqual(candy.get_mass(), 785)
 69        candy = host.remove_candy(compare_fun)
 70        self.assertEqual(candy, None)
 71
 72
 73class FluxCapacitorTest(unittest.TestCase):
 74    """Tests for the FluxCapacitor class."""
 75
 76    @timeout_decorator.timeout(0.2)
 77    def test_empty(self):
 78        """Test with empty collection."""
 79        flux_capacitor = FluxCapacitor(set())
 80        self.assertEqual(flux_capacitor.get_victim(), None)
 81
 82    @timeout_decorator.timeout(0.2)
 83    def test_empty_hosts(self):
 84        """Test with empty hosts."""
 85        kid1 = Kid((-1, -5), 1)
 86        kid2 = Kid((-6, 45), 2)
 87        flux_capacitor = FluxCapacitor({kid1, kid2})
 88        self.assertEqual(flux_capacitor.get_victim(), None)
 89
 90    @timeout_decorator.timeout(0.2)
 91    def test_empty_kids(self):
 92        """Test with empty kids."""
 93        host1 = Host((1, 3), [(1, 0.1), (3, 0.3)])
 94        host2 = Host((2, 4), [(2, 0.2), (4, 0.4)])
 95        flux_capacitor = FluxCapacitor({host1, host2})
 96        self.assertEqual(flux_capacitor.get_victim(), None)
 97
 98    @timeout_decorator.timeout(0.2)
 99    def test_no_candies(self):
100        """Test with no candies."""
101        kid1 = Kid((1, 1), 1)
102        kid2 = Kid((7, 1), 2)
103        host1 = Host((2, 1), [])
104        host2 = Host((6, 1), [])
105        flux_capacitor = FluxCapacitor({kid1, kid2, host1, host2})
106        self.assertEqual(flux_capacitor.get_victim(), None)
107
108    @timeout_decorator.timeout(0.2)
109    def test_real_case(self):
110        """Test with real case."""
111        kid1 = Kid((100, 100), 1)
112        kid2 = Kid((200, 200), 2)
113        host1 = Host((101, 101), [(21, 1.0)])
114        host2 = Host((201, 201), [(21, 1.0)])
115        self.assertEqual(FluxCapacitor({kid1, kid2, host1, host2}).get_victim(), {kid1, kid2})
116
117
118if __name__ == '__main__':
119    unittest.main()
Дискусия
Георги Кунчев
06.11.2023 08:41

Не е нужно да проверяваш дали входа е валиден. Няма да тестваме с невалиден вход. Можеш да си добавиш и допълнителни атрибути, да.
Веселина Велкова
06.11.2023 00:45

Здравейте, имам няколко въпроса :) .Първият се отнася до __init__ метода на Candy класа. Необходимо ли е да проверяваме, че количеството уран е в правилния интервал (тоест от 0 до 1) и какво да е поведението в случай, че това не е така? Вторият ми въпрос е: можем ли да си добавяме атрибути, които са ни полезни към класовете, ако това няма да влияе по никакъв начин на параметрите, които се подават на конструктора?
Георги Кунчев
05.11.2023 19:59

Надвишава е >20.
Михаил Цанков
05.11.2023 19:48

Децата търпят полуразпад при >20 или >=20? (Спорим с един колега какво значи надвишава 🫠)
Георги Кунчев
05.11.2023 17:47

Моя грешка. Преправям го. Мерси, че алармира.
Стелиан Витанов
05.11.2023 17:33

В примерния вход на реда host = Host((3,4), [(1, 1.0), (2, 3.5)]) как имаме 3.5, като това е урановото съдържание (по дефиниция нали е число между 0 и 1).
Георги Кунчев
05.11.2023 15:52

Да, това, което си пратил, изглежда като инстанция на `Kid` в `set`, т.е. изглежда правилно.
Александър Ангелов
05.11.2023 15:23

под "get_victim - връща set от инстанции на Kid" визираме {<__main__.Kid object at 0x000002152D437680>} или не?
Георги Кунчев
05.11.2023 14:29

Да.
Данаил Тодоров
05.11.2023 13:45

След като някое дете отиде при някой хост, то детето си сменя координатите, нали? После гледаме следващия най-близък хост до новите координати на детето.