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


Хелоуин в Припят
Краен срок: 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()
Дискусия
Георги Кунчев
05.11.2023 13:03

Всяко дете си има собствена кошница.
Даниела Бадева
05.11.2023 11:26

Възможно ли е няколко деца да споделят една кошница? Ако това е възможно, когато в кошницата се надвишат 20гр. уран умират всички деца или трябва броят на децата да се умножи по 20гр., за да стане количеството уран критично?
Георги Кунчев
04.11.2023 17:37

Не е проблем да имаш и други методи.
Теодор Костадинов
04.11.2023 16:48

Класовете могат ли да имат други публични методи, например class Kid -> def change_clothes()?
Георги Кунчев
04.11.2023 15:54

За този въпрос отговорът е наличен в самата дефиниция на домашното, така че ще си позволя да не отговарям.
Добромир Пеев
04.11.2023 15:41

Един домакин може ли да приема повече от 1 деца на всяка итерация ?
Георги Кунчев
04.11.2023 15:11

Добавих примерни входове в условието на задачата.
Добромир Пеев
04.11.2023 14:42

Може ли да приложите някакви примерни input-и ?
Георги Кунчев
04.11.2023 14:34

За имплементацията на `remove_candy` - трябва да работи с произволна функция. Тук методът просто очаква функция, но тя може да е каквото и да било, стига да филтрира получената колекция така, че да връща точно един елемент от нея. За имплементацията на `FluxCapacitor` вече дефинираме конкретен вид функция, която трябва да се приложи, и срещу която ще тестваме - най-голямата маса от лакомствата. Разбирам, че дефинирането на това условие би било по-удачно в тялото на класа, а не като уговорка, но е вече написано, така че няма да пипам, за да не объркам някого. Така или иначе всички уговорки са по темата `FluxCapacitor`. Останалото е строго дефинирано.
Мартин Кузманов
04.11.2023 14:02

Относно избора на конкретно сладко за дете от домакин , в "remove_candy" метода на класа "Host" се иска определящата функция да се приема външно без конкретика, докато в "Уговорки" се споменава точно коя да бъде мярката - " винаги най-тежкото по абсолютна маса сладко" . Следователно кое от двете трябва да бъде реализирано ?