1import math
2
3
4def validate_uranium(func):
5 def wrapper(self, mass, uranium):
6 if uranium < 0 or uranium > 1:
7 raise ValueError("Candy object's uranium value must be between 0 and 1 .")
8 return func(self, mass, uranium)
9
10 return wrapper
11
12
13class Candy:
14 @validate_uranium
15 def __init__(self, mass, uranium=0):
16 self.__mass = mass
17 self.__uranium = uranium
18
19 def get_uranium_quantity(self):
20 return self.__mass * self.__uranium
21
22 def get_mass(self):
23 return self.__mass
24
25
26def validate_possition(func):
27 def wrapper(self, possition):
28 if type(possition) is not tuple:
29 raise TypeError(
30 "Person object's attribute possition must be of type tuple."
31 )
32 elif len(possition) != 2:
33 raise ValueError(
34 "Person object's attribute possition must contain two coordinates."
35 )
36 return func(self, possition)
37
38 return wrapper
39
40
41class Person:
42 @validate_possition
43 def __init__(self, possition):
44 self.__possition = possition
45
46 @validate_possition
47 def set_possition(self, possition):
48 self.__possition = possition
49
50 def get_possition(self):
51 return self.__possition
52
53
54def validate_candy(func):
55 def wrapper(self, candy):
56 if type(candy) is not Candy:
57 raise TypeError("Kid object can have only Candy objects in its bag.")
58 return func(self, candy)
59
60 return wrapper
61
62
63def check_if_it_is_critital(func):
64 def wrapper(self):
65 total_uranium = 0
66 for candy in self._Kid__bag:
67 total_uranium += candy.get_uranium_quantity()
68 if total_uranium > 20:
69 return func(self, True)
70 else:
71 return func(self, False)
72
73 return wrapper
74
75
76class Kid(Person):
77 def __init__(self, possition, initiative):
78 super().__init__(possition)
79 self.__bag = set()
80 self.__initiative = initiative
81
82 def get_initiative(self):
83 return self.__initiative
84
85 @validate_candy
86 def add_candy(self, candy):
87 self.__bag.add(candy)
88
89 @check_if_it_is_critital
90 def is_critical(self, Dead=False):
91 return Dead
92
93
94def validate_host_init(func):
95 def wrapper(self, possition, candy_bag):
96 valid_candy_bag = set()
97 for candy in candy_bag:
98 if len(candy) == 2:
99 valid_candy_bag.add(Candy(*candy))
100 else:
101 raise ValueError(
102 "Host object attribute candy_bag must contains candies with two attributes."
103 )
104 return func(self, possition, valid_candy_bag)
105
106 return wrapper
107
108
109def biggest_candy(candies):
110 best_candy = max(candies, key=lambda candy: candy.get_mass())
111 return best_candy
112
113
114class Host(Person):
115 @validate_host_init
116 def __init__(self, possition, candy_bag):
117 super().__init__(possition)
118 self.__candy_bag = candy_bag
119
120 def have_candies(self):
121 return bool(self.__candy_bag)
122
123 def remove_candy(self, func):
124 if self.__candy_bag:
125 element_to_remove = func(self.__candy_bag)
126 self.__candy_bag.remove(element_to_remove)
127 return element_to_remove
128 else:
129 return None
130
131
132def calculate_distance(point1, point2):
133 x1, y1 = point1
134 x2, y2 = point2
135 return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
136
137
138def closest_neighbour(func):
139 def wrapper(self, kid):
140 closest_host = min(
141 (
142 host
143 for host in self.hosts_kids_on_the_door
144 if host not in self.kids_visited[kid]
145 ),
146 key=lambda host: (
147 calculate_distance(host.get_possition(), kid.get_possition()),
148 host.get_possition()[0],
149 host.get_possition()[1],
150 ),
151 )
152 return func(self, kid, closest_host)
153
154 return wrapper
155
156
157def when_should_stop(func):
158 def wrapper(self):
159 if self.hosts_not_out_of_candies() and self.still_unvisited_hosts():
160 return func(self, False)
161 return func(self, True)
162
163 return wrapper
164
165
166class FluxCapacitor:
167 def __init__(self, participants):
168 self.participants = participants
169 self.__init_participarts(participants)
170
171 def __init_participarts(self, participants):
172 self.kids_visited = {}
173 self.hosts_kids_on_the_door = {}
174 self.__victims = set()
175 for participant in participants:
176 if isinstance(participant, Kid):
177 self.kids_visited[participant] = set()
178 elif isinstance(participant, Host):
179 self.hosts_kids_on_the_door[participant] = set()
180
181 # we are happy if even one host have candies left,
182 # so the game makes sence to continue
183 def hosts_not_out_of_candies(self):
184 for host in self.hosts_kids_on_the_door:
185 if host.have_candies():
186 return True
187 return False
188
189 def still_unvisited_hosts(self):
190 hosts_count = len(self.hosts_kids_on_the_door)
191 for kid, visited_hosts in self.kids_visited.items():
192 if len(visited_hosts) < hosts_count:
193 return True
194 return False
195
196 @when_should_stop
197 def __should_stop(self, stop=False):
198 return stop
199
200 def __go_to_host(self, kid, host):
201 self.hosts_kids_on_the_door[host].add(kid)
202 self.kids_visited[kid].add(host)
203 kid.set_possition(host.get_possition())
204
205 @closest_neighbour
206 def __find_host(self, kid, closest_host=None):
207 return closest_host
208
209 def __hosts_give_candies(self):
210 for host, kids_on_the_door in self.hosts_kids_on_the_door.items():
211 if kids_on_the_door:
212 sorted_kids = sorted(
213 kids_on_the_door, key=lambda kid: kid.get_initiative(), reverse=True
214 )
215 for kid in sorted_kids:
216 candy = host.remove_candy(biggest_candy)
217 if candy:
218 kid.add_candy(candy)
219 self.hosts_kids_on_the_door[host].discard(
220 kid
221 ) # Remove the kid from the set
222
223 def __add_victim(self, kid):
224 self.__victims.add(kid)
225
226 def __have_victims(self):
227 if self.__victims:
228 return True
229 return False
230
231 def __collect_victims(self):
232 for kid in self.kids_visited.keys():
233 if kid.is_critical():
234 self.__add_victim(kid)
235
236 def get_victim(self):
237 while not self.__should_stop():
238 for kid in self.kids_visited:
239 host_to_go = self.__find_host(kid)
240 if host_to_go:
241 self.__go_to_host(kid, host_to_go)
242 self.__hosts_give_candies()
243 self.__collect_victims()
244 if self.__have_victims():
245 return self.__victims
246 return None
247
248
249kid1 = Kid((0, 0), 5)
250kid2 = Kid((1, 1), 3)
251host1 = Host(
252 (2, 2),
253 [
254 (10, 0.2),
255 ],
256)
257host2 = Host((3, 3), [])
258host3 = Host((4, 4), [(100, 0.1), (99, 0.5)])
259host4 = Host(
260 (5, 5),
261 [(3, 0.4)],
262)
263
264flux = FluxCapacitor({kid1, kid2, host1, host2, host3, host4})
265
266# Run the simulation
267victims = flux.get_victim()
268print("Victims:", victims)
Victims: {<solution.Kid object at 0x7f1a5632b8e0>}
........E..E
======================================================================
ERROR: test_basic_usage (test.KidTest)
Test basic usage of Kid class.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 39, in test_basic_usage
self.assertEqual(kid.get_position(), (0, 0))
AttributeError: 'Kid' object has no attribute 'get_position'
======================================================================
ERROR: test_basic_usage (test.PersonTest)
Test basic usage of Person class.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 24, in test_basic_usage
self.assertEqual(person.get_position(), (0, 0))
AttributeError: 'Person' object has no attribute 'get_position'
----------------------------------------------------------------------
Ran 12 tests in 0.001s
FAILED (errors=2)
Георги Кунчев
07.11.2023 19:02След дискусия, стана ясно, че има "typo", което е трудно да се хване при тестване, защото просто копи-пействаме код. Правя изключение и добавям двете липсващи точки.
|
f | 1 | import math | f | 1 | import math |
2 | 2 | ||||
3 | 3 | ||||
4 | def validate_uranium(func): | 4 | def validate_uranium(func): | ||
5 | def wrapper(self, mass, uranium): | 5 | def wrapper(self, mass, uranium): | ||
6 | if uranium < 0 or uranium > 1: | 6 | if uranium < 0 or uranium > 1: | ||
7 | raise ValueError("Candy object's uranium value must be between 0 and 1 .") | 7 | raise ValueError("Candy object's uranium value must be between 0 and 1 .") | ||
8 | return func(self, mass, uranium) | 8 | return func(self, mass, uranium) | ||
9 | 9 | ||||
10 | return wrapper | 10 | return wrapper | ||
11 | 11 | ||||
12 | 12 | ||||
13 | class Candy: | 13 | class Candy: | ||
14 | @validate_uranium | 14 | @validate_uranium | ||
15 | def __init__(self, mass, uranium=0): | 15 | def __init__(self, mass, uranium=0): | ||
16 | self.__mass = mass | 16 | self.__mass = mass | ||
17 | self.__uranium = uranium | 17 | self.__uranium = uranium | ||
18 | 18 | ||||
19 | def get_uranium_quantity(self): | 19 | def get_uranium_quantity(self): | ||
20 | return self.__mass * self.__uranium | 20 | return self.__mass * self.__uranium | ||
21 | 21 | ||||
22 | def get_mass(self): | 22 | def get_mass(self): | ||
23 | return self.__mass | 23 | return self.__mass | ||
24 | 24 | ||||
25 | 25 | ||||
26 | def validate_possition(func): | 26 | def validate_possition(func): | ||
27 | def wrapper(self, possition): | 27 | def wrapper(self, possition): | ||
28 | if type(possition) is not tuple: | 28 | if type(possition) is not tuple: | ||
29 | raise TypeError( | 29 | raise TypeError( | ||
30 | "Person object's attribute possition must be of type tuple." | 30 | "Person object's attribute possition must be of type tuple." | ||
31 | ) | 31 | ) | ||
32 | elif len(possition) != 2: | 32 | elif len(possition) != 2: | ||
33 | raise ValueError( | 33 | raise ValueError( | ||
34 | "Person object's attribute possition must contain two coordinates." | 34 | "Person object's attribute possition must contain two coordinates." | ||
35 | ) | 35 | ) | ||
36 | return func(self, possition) | 36 | return func(self, possition) | ||
37 | 37 | ||||
38 | return wrapper | 38 | return wrapper | ||
39 | 39 | ||||
40 | 40 | ||||
41 | class Person: | 41 | class Person: | ||
42 | @validate_possition | 42 | @validate_possition | ||
43 | def __init__(self, possition): | 43 | def __init__(self, possition): | ||
44 | self.__possition = possition | 44 | self.__possition = possition | ||
45 | 45 | ||||
46 | @validate_possition | 46 | @validate_possition | ||
47 | def set_possition(self, possition): | 47 | def set_possition(self, possition): | ||
48 | self.__possition = possition | 48 | self.__possition = possition | ||
49 | 49 | ||||
50 | def get_possition(self): | 50 | def get_possition(self): | ||
51 | return self.__possition | 51 | return self.__possition | ||
52 | 52 | ||||
53 | 53 | ||||
54 | def validate_candy(func): | 54 | def validate_candy(func): | ||
55 | def wrapper(self, candy): | 55 | def wrapper(self, candy): | ||
56 | if type(candy) is not Candy: | 56 | if type(candy) is not Candy: | ||
57 | raise TypeError("Kid object can have only Candy objects in its bag.") | 57 | raise TypeError("Kid object can have only Candy objects in its bag.") | ||
58 | return func(self, candy) | 58 | return func(self, candy) | ||
59 | 59 | ||||
60 | return wrapper | 60 | return wrapper | ||
61 | 61 | ||||
62 | 62 | ||||
63 | def check_if_it_is_critital(func): | 63 | def check_if_it_is_critital(func): | ||
64 | def wrapper(self): | 64 | def wrapper(self): | ||
65 | total_uranium = 0 | 65 | total_uranium = 0 | ||
66 | for candy in self._Kid__bag: | 66 | for candy in self._Kid__bag: | ||
67 | total_uranium += candy.get_uranium_quantity() | 67 | total_uranium += candy.get_uranium_quantity() | ||
68 | if total_uranium > 20: | 68 | if total_uranium > 20: | ||
69 | return func(self, True) | 69 | return func(self, True) | ||
70 | else: | 70 | else: | ||
71 | return func(self, False) | 71 | return func(self, False) | ||
72 | 72 | ||||
73 | return wrapper | 73 | return wrapper | ||
74 | 74 | ||||
75 | 75 | ||||
76 | class Kid(Person): | 76 | class Kid(Person): | ||
77 | def __init__(self, possition, initiative): | 77 | def __init__(self, possition, initiative): | ||
78 | super().__init__(possition) | 78 | super().__init__(possition) | ||
79 | self.__bag = set() | 79 | self.__bag = set() | ||
80 | self.__initiative = initiative | 80 | self.__initiative = initiative | ||
81 | 81 | ||||
82 | def get_initiative(self): | 82 | def get_initiative(self): | ||
83 | return self.__initiative | 83 | return self.__initiative | ||
84 | 84 | ||||
85 | @validate_candy | 85 | @validate_candy | ||
86 | def add_candy(self, candy): | 86 | def add_candy(self, candy): | ||
87 | self.__bag.add(candy) | 87 | self.__bag.add(candy) | ||
88 | 88 | ||||
89 | @check_if_it_is_critital | 89 | @check_if_it_is_critital | ||
90 | def is_critical(self, Dead=False): | 90 | def is_critical(self, Dead=False): | ||
91 | return Dead | 91 | return Dead | ||
92 | 92 | ||||
93 | 93 | ||||
94 | def validate_host_init(func): | 94 | def validate_host_init(func): | ||
95 | def wrapper(self, possition, candy_bag): | 95 | def wrapper(self, possition, candy_bag): | ||
96 | valid_candy_bag = set() | 96 | valid_candy_bag = set() | ||
97 | for candy in candy_bag: | 97 | for candy in candy_bag: | ||
98 | if len(candy) == 2: | 98 | if len(candy) == 2: | ||
99 | valid_candy_bag.add(Candy(*candy)) | 99 | valid_candy_bag.add(Candy(*candy)) | ||
100 | else: | 100 | else: | ||
101 | raise ValueError( | 101 | raise ValueError( | ||
102 | "Host object attribute candy_bag must contains candies with two attributes." | 102 | "Host object attribute candy_bag must contains candies with two attributes." | ||
103 | ) | 103 | ) | ||
104 | return func(self, possition, valid_candy_bag) | 104 | return func(self, possition, valid_candy_bag) | ||
105 | 105 | ||||
106 | return wrapper | 106 | return wrapper | ||
107 | 107 | ||||
108 | 108 | ||||
109 | def biggest_candy(candies): | 109 | def biggest_candy(candies): | ||
110 | best_candy = max(candies, key=lambda candy: candy.get_mass()) | 110 | best_candy = max(candies, key=lambda candy: candy.get_mass()) | ||
111 | return best_candy | 111 | return best_candy | ||
112 | 112 | ||||
113 | 113 | ||||
114 | class Host(Person): | 114 | class Host(Person): | ||
115 | @validate_host_init | 115 | @validate_host_init | ||
116 | def __init__(self, possition, candy_bag): | 116 | def __init__(self, possition, candy_bag): | ||
117 | super().__init__(possition) | 117 | super().__init__(possition) | ||
118 | self.__candy_bag = candy_bag | 118 | self.__candy_bag = candy_bag | ||
119 | 119 | ||||
120 | def have_candies(self): | 120 | def have_candies(self): | ||
121 | return bool(self.__candy_bag) | 121 | return bool(self.__candy_bag) | ||
122 | 122 | ||||
123 | def remove_candy(self, func): | 123 | def remove_candy(self, func): | ||
124 | if self.__candy_bag: | 124 | if self.__candy_bag: | ||
125 | element_to_remove = func(self.__candy_bag) | 125 | element_to_remove = func(self.__candy_bag) | ||
126 | self.__candy_bag.remove(element_to_remove) | 126 | self.__candy_bag.remove(element_to_remove) | ||
127 | return element_to_remove | 127 | return element_to_remove | ||
128 | else: | 128 | else: | ||
129 | return None | 129 | return None | ||
130 | 130 | ||||
131 | 131 | ||||
132 | def calculate_distance(point1, point2): | 132 | def calculate_distance(point1, point2): | ||
133 | x1, y1 = point1 | 133 | x1, y1 = point1 | ||
134 | x2, y2 = point2 | 134 | x2, y2 = point2 | ||
135 | return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) | 135 | return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) | ||
136 | 136 | ||||
137 | 137 | ||||
138 | def closest_neighbour(func): | 138 | def closest_neighbour(func): | ||
139 | def wrapper(self, kid): | 139 | def wrapper(self, kid): | ||
140 | closest_host = min( | 140 | closest_host = min( | ||
141 | ( | 141 | ( | ||
142 | host | 142 | host | ||
143 | for host in self.hosts_kids_on_the_door | 143 | for host in self.hosts_kids_on_the_door | ||
144 | if host not in self.kids_visited[kid] | 144 | if host not in self.kids_visited[kid] | ||
145 | ), | 145 | ), | ||
146 | key=lambda host: ( | 146 | key=lambda host: ( | ||
147 | calculate_distance(host.get_possition(), kid.get_possition()), | 147 | calculate_distance(host.get_possition(), kid.get_possition()), | ||
148 | host.get_possition()[0], | 148 | host.get_possition()[0], | ||
149 | host.get_possition()[1], | 149 | host.get_possition()[1], | ||
150 | ), | 150 | ), | ||
151 | ) | 151 | ) | ||
152 | return func(self, kid, closest_host) | 152 | return func(self, kid, closest_host) | ||
153 | 153 | ||||
154 | return wrapper | 154 | return wrapper | ||
155 | 155 | ||||
156 | 156 | ||||
157 | def when_should_stop(func): | 157 | def when_should_stop(func): | ||
158 | def wrapper(self): | 158 | def wrapper(self): | ||
159 | if self.hosts_not_out_of_candies() and self.still_unvisited_hosts(): | 159 | if self.hosts_not_out_of_candies() and self.still_unvisited_hosts(): | ||
160 | return func(self, False) | 160 | return func(self, False) | ||
161 | return func(self, True) | 161 | return func(self, True) | ||
162 | 162 | ||||
163 | return wrapper | 163 | return wrapper | ||
164 | 164 | ||||
165 | 165 | ||||
166 | class FluxCapacitor: | 166 | class FluxCapacitor: | ||
167 | def __init__(self, participants): | 167 | def __init__(self, participants): | ||
168 | self.participants = participants | 168 | self.participants = participants | ||
169 | self.__init_participarts(participants) | 169 | self.__init_participarts(participants) | ||
170 | 170 | ||||
171 | def __init_participarts(self, participants): | 171 | def __init_participarts(self, participants): | ||
172 | self.kids_visited = {} | 172 | self.kids_visited = {} | ||
173 | self.hosts_kids_on_the_door = {} | 173 | self.hosts_kids_on_the_door = {} | ||
174 | self.__victims = set() | 174 | self.__victims = set() | ||
175 | for participant in participants: | 175 | for participant in participants: | ||
176 | if isinstance(participant, Kid): | 176 | if isinstance(participant, Kid): | ||
177 | self.kids_visited[participant] = set() | 177 | self.kids_visited[participant] = set() | ||
178 | elif isinstance(participant, Host): | 178 | elif isinstance(participant, Host): | ||
179 | self.hosts_kids_on_the_door[participant] = set() | 179 | self.hosts_kids_on_the_door[participant] = set() | ||
180 | 180 | ||||
181 | # we are happy if even one host have candies left, | 181 | # we are happy if even one host have candies left, | ||
182 | # so the game makes sence to continue | 182 | # so the game makes sence to continue | ||
183 | def hosts_not_out_of_candies(self): | 183 | def hosts_not_out_of_candies(self): | ||
n | 184 | for host in self.hosts_kids_on_the_door.keys(): | n | 184 | for host in self.hosts_kids_on_the_door: |
185 | if host.have_candies(): | 185 | if host.have_candies(): | ||
186 | return True | 186 | return True | ||
187 | return False | 187 | return False | ||
188 | 188 | ||||
189 | def still_unvisited_hosts(self): | 189 | def still_unvisited_hosts(self): | ||
190 | hosts_count = len(self.hosts_kids_on_the_door) | 190 | hosts_count = len(self.hosts_kids_on_the_door) | ||
191 | for kid, visited_hosts in self.kids_visited.items(): | 191 | for kid, visited_hosts in self.kids_visited.items(): | ||
192 | if len(visited_hosts) < hosts_count: | 192 | if len(visited_hosts) < hosts_count: | ||
193 | return True | 193 | return True | ||
194 | return False | 194 | return False | ||
195 | 195 | ||||
196 | @when_should_stop | 196 | @when_should_stop | ||
197 | def __should_stop(self, stop=False): | 197 | def __should_stop(self, stop=False): | ||
198 | return stop | 198 | return stop | ||
199 | 199 | ||||
200 | def __go_to_host(self, kid, host): | 200 | def __go_to_host(self, kid, host): | ||
201 | self.hosts_kids_on_the_door[host].add(kid) | 201 | self.hosts_kids_on_the_door[host].add(kid) | ||
202 | self.kids_visited[kid].add(host) | 202 | self.kids_visited[kid].add(host) | ||
203 | kid.set_possition(host.get_possition()) | 203 | kid.set_possition(host.get_possition()) | ||
204 | 204 | ||||
205 | @closest_neighbour | 205 | @closest_neighbour | ||
206 | def __find_host(self, kid, closest_host=None): | 206 | def __find_host(self, kid, closest_host=None): | ||
207 | return closest_host | 207 | return closest_host | ||
208 | 208 | ||||
209 | def __hosts_give_candies(self): | 209 | def __hosts_give_candies(self): | ||
210 | for host, kids_on_the_door in self.hosts_kids_on_the_door.items(): | 210 | for host, kids_on_the_door in self.hosts_kids_on_the_door.items(): | ||
211 | if kids_on_the_door: | 211 | if kids_on_the_door: | ||
212 | sorted_kids = sorted( | 212 | sorted_kids = sorted( | ||
213 | kids_on_the_door, key=lambda kid: kid.get_initiative(), reverse=True | 213 | kids_on_the_door, key=lambda kid: kid.get_initiative(), reverse=True | ||
214 | ) | 214 | ) | ||
215 | for kid in sorted_kids: | 215 | for kid in sorted_kids: | ||
216 | candy = host.remove_candy(biggest_candy) | 216 | candy = host.remove_candy(biggest_candy) | ||
217 | if candy: | 217 | if candy: | ||
218 | kid.add_candy(candy) | 218 | kid.add_candy(candy) | ||
219 | self.hosts_kids_on_the_door[host].discard( | 219 | self.hosts_kids_on_the_door[host].discard( | ||
220 | kid | 220 | kid | ||
221 | ) # Remove the kid from the set | 221 | ) # Remove the kid from the set | ||
222 | 222 | ||||
223 | def __add_victim(self, kid): | 223 | def __add_victim(self, kid): | ||
224 | self.__victims.add(kid) | 224 | self.__victims.add(kid) | ||
225 | 225 | ||||
n | 226 | def __we_have_victims(self): | n | 226 | def __have_victims(self): |
227 | if self.__victims: | 227 | if self.__victims: | ||
228 | return True | 228 | return True | ||
229 | return False | 229 | return False | ||
230 | 230 | ||||
231 | def __collect_victims(self): | 231 | def __collect_victims(self): | ||
232 | for kid in self.kids_visited.keys(): | 232 | for kid in self.kids_visited.keys(): | ||
233 | if kid.is_critical(): | 233 | if kid.is_critical(): | ||
234 | self.__add_victim(kid) | 234 | self.__add_victim(kid) | ||
235 | 235 | ||||
236 | def get_victim(self): | 236 | def get_victim(self): | ||
237 | while not self.__should_stop(): | 237 | while not self.__should_stop(): | ||
n | 238 | for kid in self.kids_visited.keys(): | n | 238 | for kid in self.kids_visited: |
239 | host_to_go = self.__find_host(kid) | 239 | host_to_go = self.__find_host(kid) | ||
240 | if host_to_go: | 240 | if host_to_go: | ||
241 | self.__go_to_host(kid, host_to_go) | 241 | self.__go_to_host(kid, host_to_go) | ||
242 | self.__hosts_give_candies() | 242 | self.__hosts_give_candies() | ||
243 | self.__collect_victims() | 243 | self.__collect_victims() | ||
n | 244 | if self.__we_have_victims(): | n | 244 | if self.__have_victims(): |
245 | return self.__victims | 245 | return self.__victims | ||
246 | return None | 246 | return None | ||
247 | 247 | ||||
248 | 248 | ||||
249 | kid1 = Kid((0, 0), 5) | 249 | kid1 = Kid((0, 0), 5) | ||
250 | kid2 = Kid((1, 1), 3) | 250 | kid2 = Kid((1, 1), 3) | ||
251 | host1 = Host( | 251 | host1 = Host( | ||
252 | (2, 2), | 252 | (2, 2), | ||
253 | [ | 253 | [ | ||
254 | (10, 0.2), | 254 | (10, 0.2), | ||
255 | ], | 255 | ], | ||
256 | ) | 256 | ) | ||
257 | host2 = Host((3, 3), []) | 257 | host2 = Host((3, 3), []) | ||
t | 258 | host3 = Host((4, 4), []) | t | 258 | host3 = Host((4, 4), [(100, 0.1), (99, 0.5)]) |
259 | host4 = Host( | 259 | host4 = Host( | ||
260 | (5, 5), | 260 | (5, 5), | ||
261 | [(3, 0.4)], | 261 | [(3, 0.4)], | ||
262 | ) | 262 | ) | ||
263 | 263 | ||||
264 | flux = FluxCapacitor({kid1, kid2, host1, host2, host3, host4}) | 264 | flux = FluxCapacitor({kid1, kid2, host1, host2, host3, host4}) | ||
265 | 265 | ||||
266 | # Run the simulation | 266 | # Run the simulation | ||
267 | victims = flux.get_victim() | 267 | victims = flux.get_victim() | ||
268 | print("Victims:", victims) | 268 | print("Victims:", victims) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | import math | f | 1 | import math |
2 | 2 | ||||
3 | 3 | ||||
4 | def validate_uranium(func): | 4 | def validate_uranium(func): | ||
5 | def wrapper(self, mass, uranium): | 5 | def wrapper(self, mass, uranium): | ||
6 | if uranium < 0 or uranium > 1: | 6 | if uranium < 0 or uranium > 1: | ||
7 | raise ValueError("Candy object's uranium value must be between 0 and 1 .") | 7 | raise ValueError("Candy object's uranium value must be between 0 and 1 .") | ||
8 | return func(self, mass, uranium) | 8 | return func(self, mass, uranium) | ||
9 | 9 | ||||
10 | return wrapper | 10 | return wrapper | ||
11 | 11 | ||||
12 | 12 | ||||
13 | class Candy: | 13 | class Candy: | ||
14 | @validate_uranium | 14 | @validate_uranium | ||
15 | def __init__(self, mass, uranium=0): | 15 | def __init__(self, mass, uranium=0): | ||
16 | self.__mass = mass | 16 | self.__mass = mass | ||
17 | self.__uranium = uranium | 17 | self.__uranium = uranium | ||
18 | 18 | ||||
19 | def get_uranium_quantity(self): | 19 | def get_uranium_quantity(self): | ||
20 | return self.__mass * self.__uranium | 20 | return self.__mass * self.__uranium | ||
21 | 21 | ||||
22 | def get_mass(self): | 22 | def get_mass(self): | ||
23 | return self.__mass | 23 | return self.__mass | ||
24 | 24 | ||||
25 | 25 | ||||
26 | def validate_possition(func): | 26 | def validate_possition(func): | ||
27 | def wrapper(self, possition): | 27 | def wrapper(self, possition): | ||
28 | if type(possition) is not tuple: | 28 | if type(possition) is not tuple: | ||
29 | raise TypeError( | 29 | raise TypeError( | ||
30 | "Person object's attribute possition must be of type tuple." | 30 | "Person object's attribute possition must be of type tuple." | ||
31 | ) | 31 | ) | ||
32 | elif len(possition) != 2: | 32 | elif len(possition) != 2: | ||
33 | raise ValueError( | 33 | raise ValueError( | ||
34 | "Person object's attribute possition must contain two coordinates." | 34 | "Person object's attribute possition must contain two coordinates." | ||
35 | ) | 35 | ) | ||
36 | return func(self, possition) | 36 | return func(self, possition) | ||
37 | 37 | ||||
38 | return wrapper | 38 | return wrapper | ||
39 | 39 | ||||
40 | 40 | ||||
41 | class Person: | 41 | class Person: | ||
42 | @validate_possition | 42 | @validate_possition | ||
43 | def __init__(self, possition): | 43 | def __init__(self, possition): | ||
44 | self.__possition = possition | 44 | self.__possition = possition | ||
45 | 45 | ||||
46 | @validate_possition | 46 | @validate_possition | ||
47 | def set_possition(self, possition): | 47 | def set_possition(self, possition): | ||
48 | self.__possition = possition | 48 | self.__possition = possition | ||
49 | 49 | ||||
50 | def get_possition(self): | 50 | def get_possition(self): | ||
51 | return self.__possition | 51 | return self.__possition | ||
52 | 52 | ||||
53 | 53 | ||||
54 | def validate_candy(func): | 54 | def validate_candy(func): | ||
55 | def wrapper(self, candy): | 55 | def wrapper(self, candy): | ||
56 | if type(candy) is not Candy: | 56 | if type(candy) is not Candy: | ||
57 | raise TypeError("Kid object can have only Candy objects in its bag.") | 57 | raise TypeError("Kid object can have only Candy objects in its bag.") | ||
58 | return func(self, candy) | 58 | return func(self, candy) | ||
59 | 59 | ||||
60 | return wrapper | 60 | return wrapper | ||
61 | 61 | ||||
62 | 62 | ||||
63 | def check_if_it_is_critital(func): | 63 | def check_if_it_is_critital(func): | ||
64 | def wrapper(self): | 64 | def wrapper(self): | ||
65 | total_uranium = 0 | 65 | total_uranium = 0 | ||
66 | for candy in self._Kid__bag: | 66 | for candy in self._Kid__bag: | ||
67 | total_uranium += candy.get_uranium_quantity() | 67 | total_uranium += candy.get_uranium_quantity() | ||
68 | if total_uranium > 20: | 68 | if total_uranium > 20: | ||
69 | return func(self, True) | 69 | return func(self, True) | ||
70 | else: | 70 | else: | ||
71 | return func(self, False) | 71 | return func(self, False) | ||
72 | 72 | ||||
73 | return wrapper | 73 | return wrapper | ||
74 | 74 | ||||
75 | 75 | ||||
76 | class Kid(Person): | 76 | class Kid(Person): | ||
77 | def __init__(self, possition, initiative): | 77 | def __init__(self, possition, initiative): | ||
78 | super().__init__(possition) | 78 | super().__init__(possition) | ||
79 | self.__bag = set() | 79 | self.__bag = set() | ||
80 | self.__initiative = initiative | 80 | self.__initiative = initiative | ||
81 | 81 | ||||
82 | def get_initiative(self): | 82 | def get_initiative(self): | ||
83 | return self.__initiative | 83 | return self.__initiative | ||
84 | 84 | ||||
85 | @validate_candy | 85 | @validate_candy | ||
86 | def add_candy(self, candy): | 86 | def add_candy(self, candy): | ||
87 | self.__bag.add(candy) | 87 | self.__bag.add(candy) | ||
88 | 88 | ||||
89 | @check_if_it_is_critital | 89 | @check_if_it_is_critital | ||
90 | def is_critical(self, Dead=False): | 90 | def is_critical(self, Dead=False): | ||
91 | return Dead | 91 | return Dead | ||
92 | 92 | ||||
93 | 93 | ||||
94 | def validate_host_init(func): | 94 | def validate_host_init(func): | ||
95 | def wrapper(self, possition, candy_bag): | 95 | def wrapper(self, possition, candy_bag): | ||
96 | valid_candy_bag = set() | 96 | valid_candy_bag = set() | ||
97 | for candy in candy_bag: | 97 | for candy in candy_bag: | ||
98 | if len(candy) == 2: | 98 | if len(candy) == 2: | ||
99 | valid_candy_bag.add(Candy(*candy)) | 99 | valid_candy_bag.add(Candy(*candy)) | ||
100 | else: | 100 | else: | ||
101 | raise ValueError( | 101 | raise ValueError( | ||
102 | "Host object attribute candy_bag must contains candies with two attributes." | 102 | "Host object attribute candy_bag must contains candies with two attributes." | ||
103 | ) | 103 | ) | ||
104 | return func(self, possition, valid_candy_bag) | 104 | return func(self, possition, valid_candy_bag) | ||
105 | 105 | ||||
106 | return wrapper | 106 | return wrapper | ||
107 | 107 | ||||
108 | 108 | ||||
109 | def biggest_candy(candies): | 109 | def biggest_candy(candies): | ||
n | 110 | max_weigth = 0 | n | 110 | best_candy = max(candies, key=lambda candy: candy.get_mass()) |
111 | best_candy = None | ||||
112 | for candy in candies: | ||||
113 | current_weigth = candy.get_mass() | ||||
114 | if current_weigth > max_weigth: | ||||
115 | max_weigth = current_weigth | ||||
116 | best_candy = candy | ||||
117 | return best_candy | 111 | return best_candy | ||
118 | 112 | ||||
119 | 113 | ||||
120 | class Host(Person): | 114 | class Host(Person): | ||
121 | @validate_host_init | 115 | @validate_host_init | ||
122 | def __init__(self, possition, candy_bag): | 116 | def __init__(self, possition, candy_bag): | ||
123 | super().__init__(possition) | 117 | super().__init__(possition) | ||
124 | self.__candy_bag = candy_bag | 118 | self.__candy_bag = candy_bag | ||
125 | 119 | ||||
126 | def have_candies(self): | 120 | def have_candies(self): | ||
127 | return bool(self.__candy_bag) | 121 | return bool(self.__candy_bag) | ||
128 | 122 | ||||
129 | def remove_candy(self, func): | 123 | def remove_candy(self, func): | ||
130 | if self.__candy_bag: | 124 | if self.__candy_bag: | ||
131 | element_to_remove = func(self.__candy_bag) | 125 | element_to_remove = func(self.__candy_bag) | ||
132 | self.__candy_bag.remove(element_to_remove) | 126 | self.__candy_bag.remove(element_to_remove) | ||
133 | return element_to_remove | 127 | return element_to_remove | ||
134 | else: | 128 | else: | ||
135 | return None | 129 | return None | ||
136 | 130 | ||||
137 | 131 | ||||
138 | def calculate_distance(point1, point2): | 132 | def calculate_distance(point1, point2): | ||
n | 139 | x1, y1 = point1() | n | 133 | x1, y1 = point1 |
140 | x2, y2 = point2() | 134 | x2, y2 = point2 | ||
141 | return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) | 135 | return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) | ||
142 | 136 | ||||
143 | 137 | ||||
144 | def closest_neighbour(func): | 138 | def closest_neighbour(func): | ||
145 | def wrapper(self, kid): | 139 | def wrapper(self, kid): | ||
n | 146 | closest_host = None | n | 140 | closest_host = min( |
147 | min_distance = float("inf") | 141 | ( | ||
142 | host | ||||
148 | for current_host in self.hosts_kids_on_the_door.keys(): | 143 | for host in self.hosts_kids_on_the_door | ||
149 | if current_host not in self.kids_visited[kid]: | 144 | if host not in self.kids_visited[kid] | ||
150 | current_distance = calculate_distance( | ||||
151 | kid.get_possition, current_host.get_possition | ||||
152 | ) | 145 | ), | ||
153 | if current_distance < min_distance: | 146 | key=lambda host: ( | ||
154 | min_distance = current_distance | 147 | calculate_distance(host.get_possition(), kid.get_possition()), | ||
155 | closest_host = current_host | 148 | host.get_possition()[0], | ||
156 | elif current_distance == min_distance: | 149 | host.get_possition()[1], | ||
157 | current_x, current_y = current_host.get_possition | 150 | ), | ||
158 | current_best_x, current_best_y = closest_host.get_possition | 151 | ) | ||
159 | if current_x < current_best_x: | ||||
160 | closest_host = current_host | ||||
161 | elif current_x == current_best_x: | ||||
162 | if current_y < current_best_y: | ||||
163 | closest_host = current_host | ||||
164 | return func(self, kid, closest_host) | 152 | return func(self, kid, closest_host) | ||
165 | 153 | ||||
166 | return wrapper | 154 | return wrapper | ||
167 | 155 | ||||
168 | 156 | ||||
n | 169 | def when_should_we_stop(func): | n | 157 | def when_should_stop(func): |
170 | def wrapper(self): | 158 | def wrapper(self): | ||
171 | if self.hosts_not_out_of_candies() and self.still_unvisited_hosts(): | 159 | if self.hosts_not_out_of_candies() and self.still_unvisited_hosts(): | ||
172 | return func(self, False) | 160 | return func(self, False) | ||
173 | return func(self, True) | 161 | return func(self, True) | ||
174 | 162 | ||||
175 | return wrapper | 163 | return wrapper | ||
176 | 164 | ||||
177 | 165 | ||||
178 | class FluxCapacitor: | 166 | class FluxCapacitor: | ||
179 | def __init__(self, participants): | 167 | def __init__(self, participants): | ||
180 | self.participants = participants | 168 | self.participants = participants | ||
n | 181 | self.__init_participarts__(participants) | n | 169 | self.__init_participarts(participants) |
182 | 170 | ||||
n | 183 | def __init_participarts__(self, participants): | n | 171 | def __init_participarts(self, participants): |
184 | self.kids_visited = {} | 172 | self.kids_visited = {} | ||
185 | self.hosts_kids_on_the_door = {} | 173 | self.hosts_kids_on_the_door = {} | ||
n | 186 | self.__victims__ = set() | n | 174 | self.__victims = set() |
187 | for participant in participants: | 175 | for participant in participants: | ||
188 | if isinstance(participant, Kid): | 176 | if isinstance(participant, Kid): | ||
189 | self.kids_visited[participant] = set() | 177 | self.kids_visited[participant] = set() | ||
190 | elif isinstance(participant, Host): | 178 | elif isinstance(participant, Host): | ||
191 | self.hosts_kids_on_the_door[participant] = set() | 179 | self.hosts_kids_on_the_door[participant] = set() | ||
192 | 180 | ||||
193 | # we are happy if even one host have candies left, | 181 | # we are happy if even one host have candies left, | ||
194 | # so the game makes sence to continue | 182 | # so the game makes sence to continue | ||
195 | def hosts_not_out_of_candies(self): | 183 | def hosts_not_out_of_candies(self): | ||
196 | for host in self.hosts_kids_on_the_door.keys(): | 184 | for host in self.hosts_kids_on_the_door.keys(): | ||
197 | if host.have_candies(): | 185 | if host.have_candies(): | ||
198 | return True | 186 | return True | ||
199 | return False | 187 | return False | ||
200 | 188 | ||||
201 | def still_unvisited_hosts(self): | 189 | def still_unvisited_hosts(self): | ||
202 | hosts_count = len(self.hosts_kids_on_the_door) | 190 | hosts_count = len(self.hosts_kids_on_the_door) | ||
203 | for kid, visited_hosts in self.kids_visited.items(): | 191 | for kid, visited_hosts in self.kids_visited.items(): | ||
204 | if len(visited_hosts) < hosts_count: | 192 | if len(visited_hosts) < hosts_count: | ||
205 | return True | 193 | return True | ||
206 | return False | 194 | return False | ||
207 | 195 | ||||
n | 208 | @when_should_we_stop | n | 196 | @when_should_stop |
209 | def __we_should_stop__(self, stop=False): | 197 | def __should_stop(self, stop=False): | ||
210 | return stop | 198 | return stop | ||
211 | 199 | ||||
n | 212 | def __go_to_host__(self, kid, host): | n | 200 | def __go_to_host(self, kid, host): |
213 | self.hosts_kids_on_the_door[host].add(kid) | 201 | self.hosts_kids_on_the_door[host].add(kid) | ||
214 | self.kids_visited[kid].add(host) | 202 | self.kids_visited[kid].add(host) | ||
215 | kid.set_possition(host.get_possition()) | 203 | kid.set_possition(host.get_possition()) | ||
216 | 204 | ||||
217 | @closest_neighbour | 205 | @closest_neighbour | ||
n | 218 | def __find_host__(self, kid, closest_host=None): | n | 206 | def __find_host(self, kid, closest_host=None): |
219 | return closest_host | 207 | return closest_host | ||
220 | 208 | ||||
n | 221 | def __hosts_give_candies__(self): | n | 209 | def __hosts_give_candies(self): |
222 | for host, kids_on_the_door in self.hosts_kids_on_the_door.items(): | 210 | for host, kids_on_the_door in self.hosts_kids_on_the_door.items(): | ||
223 | if kids_on_the_door: | 211 | if kids_on_the_door: | ||
224 | sorted_kids = sorted( | 212 | sorted_kids = sorted( | ||
225 | kids_on_the_door, key=lambda kid: kid.get_initiative(), reverse=True | 213 | kids_on_the_door, key=lambda kid: kid.get_initiative(), reverse=True | ||
226 | ) | 214 | ) | ||
227 | for kid in sorted_kids: | 215 | for kid in sorted_kids: | ||
228 | candy = host.remove_candy(biggest_candy) | 216 | candy = host.remove_candy(biggest_candy) | ||
229 | if candy: | 217 | if candy: | ||
230 | kid.add_candy(candy) | 218 | kid.add_candy(candy) | ||
231 | self.hosts_kids_on_the_door[host].discard( | 219 | self.hosts_kids_on_the_door[host].discard( | ||
232 | kid | 220 | kid | ||
233 | ) # Remove the kid from the set | 221 | ) # Remove the kid from the set | ||
234 | 222 | ||||
n | 235 | def __add_victim__(self, kid): | n | 223 | def __add_victim(self, kid): |
236 | self.__victims__.add(kid) | 224 | self.__victims.add(kid) | ||
237 | 225 | ||||
n | 238 | def __we_have_victims__(self): | n | 226 | def __we_have_victims(self): |
239 | if self.__victims__: | 227 | if self.__victims: | ||
240 | return True | 228 | return True | ||
241 | return False | 229 | return False | ||
242 | 230 | ||||
n | 243 | def __collect_victims__(self): | n | 231 | def __collect_victims(self): |
244 | for kid in self.kids_visited.keys(): | 232 | for kid in self.kids_visited.keys(): | ||
245 | if kid.is_critical(): | 233 | if kid.is_critical(): | ||
n | 246 | self.__add_victim__(kid) | n | 234 | self.__add_victim(kid) |
247 | 235 | ||||
248 | def get_victim(self): | 236 | def get_victim(self): | ||
n | 249 | while not self.__we_should_stop__(): | n | 237 | while not self.__should_stop(): |
250 | for kid in self.kids_visited.keys(): | 238 | for kid in self.kids_visited.keys(): | ||
n | 251 | host_to_go = self.__find_host__(kid) | n | 239 | host_to_go = self.__find_host(kid) |
252 | if host_to_go: | 240 | if host_to_go: | ||
n | 253 | self.__go_to_host__(kid, host_to_go) | n | 241 | self.__go_to_host(kid, host_to_go) |
254 | self.__hosts_give_candies__() | 242 | self.__hosts_give_candies() | ||
255 | self.__collect_victims__() | 243 | self.__collect_victims() | ||
256 | if self.__we_have_victims__(): | 244 | if self.__we_have_victims(): | ||
257 | return self.__victims__ | 245 | return self.__victims | ||
258 | return None | 246 | return None | ||
259 | 247 | ||||
260 | 248 | ||||
261 | kid1 = Kid((0, 0), 5) | 249 | kid1 = Kid((0, 0), 5) | ||
262 | kid2 = Kid((1, 1), 3) | 250 | kid2 = Kid((1, 1), 3) | ||
263 | host1 = Host( | 251 | host1 = Host( | ||
264 | (2, 2), | 252 | (2, 2), | ||
265 | [ | 253 | [ | ||
266 | (10, 0.2), | 254 | (10, 0.2), | ||
267 | ], | 255 | ], | ||
268 | ) | 256 | ) | ||
269 | host2 = Host((3, 3), []) | 257 | host2 = Host((3, 3), []) | ||
270 | host3 = Host((4, 4), []) | 258 | host3 = Host((4, 4), []) | ||
271 | host4 = Host( | 259 | host4 = Host( | ||
272 | (5, 5), | 260 | (5, 5), | ||
t | 273 | [(3, 0.4), (37, 0.5), (25, 0.9)], | t | 261 | [(3, 0.4)], |
274 | ) | 262 | ) | ||
275 | 263 | ||||
276 | flux = FluxCapacitor({kid1, kid2, host1, host2, host3, host4}) | 264 | flux = FluxCapacitor({kid1, kid2, host1, host2, host3, host4}) | ||
277 | 265 | ||||
278 | # Run the simulation | 266 | # Run the simulation | ||
279 | victims = flux.get_victim() | 267 | victims = flux.get_victim() | ||
280 | print("Victims:", victims) | 268 | print("Victims:", victims) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|