1"""
2Solution to homework 3 for Introduction to Python course at FMI
3by Nikolay Nikolaev.
4"""
5import sys
6
7
8class Candy:
9 """
10 Defines candy objects that have mass and uranium percentage. The two
11 methods are getters for the class values.
12 """
13 def __init__(self, mass, uranium):
14 self.mass = mass
15 self.uranium = uranium
16
17 def get_uranium_quantity(self):
18 """Returns the mass of the uranium in the candy."""
19 return self.mass * self.uranium
20
21 def get_mass(self):
22 """Returns the mass of the candy."""
23 return self.mass
24
25
26class Person:
27 """
28 Defines person object who is positioned in position. The two
29 methods are a setter and a getter for the position.
30 """
31 def __init__(self, position):
32 self.position = position
33
34 def get_position(self):
35 """"Returns the position of the person."""
36 return self.position
37
38 def set_position(self, position):
39 """"Sets the position of the person."""
40 self.position = position
41
42
43class Kid(Person):
44 """
45 Defines kid object that is inherited from Person. The initiative
46 attribute is used for sorting purposes of the kids. They can add
47 candy objects in their baskets.
48 """
49 def __init__(self, position, initiative):
50 super().__init__(position)
51 self.initiative = initiative
52 self.candy_basket = []
53
54 def get_initiative(self):
55 """Returns the initiative of the kid."""
56 return self.initiative
57
58 def add_candy(self, candy):
59 """Adds a Candy object to the candy_basket of the kid."""
60 self.candy_basket.append(candy)
61
62 def is_critical(self):
63 """
64 Return True if the mass of the uranium in the basket is
65 bigger than 20 grams.
66 """
67 uranium_quantity = 0
68 for candy in self.candy_basket:
69 uranium_quantity += candy.get_uranium_quantity()
70 return uranium_quantity > 20
71
72
73class Host(Person):
74 """
75 Defines host objects that are inherited from Person. They hold a
76 basket of Candy objects which they can remove.
77 """
78 def __init__(self, position, candies):
79 super().__init__(position)
80 self.candy_basket = [Candy(mass, uranium) for mass, uranium in candies]
81
82 def remove_candy(self, determining_function):
83 """Removes a candy from the host's candy basket and retruns it."""
84 if self.candy_basket:
85 chosen_candy = determining_function(self.candy_basket)
86 self.candy_basket.remove(chosen_candy)
87 return chosen_candy
88 return None
89
90
91class FluxCapacitor:
92 """
93 Organises a scenario where the participants are kids and hosts. The kids
94 collect candies from hosts. Whenever a kid/kids collect/s a critical mass
95 of uranium, all the hosts are visited by all the kids, or all candies of
96 the hosts baskets are collected - the simulation stops. In the first case
97 the victims are returned and in the other two - None.
98 """
99 def __init__(self, participants):
100 self.participants = participants
101
102 def get_victim(self):
103 """
104 Simulates the events of kids collecting candies of hosts. Returns
105 the victims if there are any. Otherwise, returns None.
106 """
107 kids = {kid for kid in self.participants if isinstance(kid, Kid)}
108 hosts = {host for host in self.participants if isinstance(host, Host)}
109 is_visited_by_all_kids = {host:[] for host in hosts}
110
111 while True:
112 victims = set()
113
114 for kid in kids:
115 if kid.is_critical():
116 victims.add(kid)
117
118 # If there is at least one victim, return it
119 if victims:
120 return victims
121
122 # If all hosts don't have candies or were
123 # visited by all kids, return None
124 if not hosts:
125 return None
126
127 # The variable current_visitors stores all the
128 # kids that are at each host at the current iteration.
129 current_visitors = {host:[] for host in hosts}
130 for kid in kids:
131 minimum_distance = sys.float_info.max
132 next_host = None
133
134 # Finds the nearest host for each kid without repetition
135 for host in hosts:
136 if kid not in is_visited_by_all_kids[host]:
137 distance = calculate_distance(kid, host)
138 if distance == minimum_distance:
139 next_host = determine_better_host(host, next_host)
140 if distance < minimum_distance:
141 minimum_distance = distance
142 next_host = host
143
144 if next_host is not None:
145 current_visitors[next_host].append(kid)
146 kid.set_position(next_host.get_position())
147
148 hosts_to_remove = []
149 for host in hosts:
150 # Sort kids by their initiative
151 current_visitors[host] = sorted(current_visitors[host],
152 key=lambda kid: kid.get_initiative(), reverse = True)
153
154 is_visited_by_all_kids[host] += current_visitors[host]
155 # and give them candy.
156 for kid in current_visitors[host]:
157 candy = host.remove_candy(determine_max_mass)
158 if candy:
159 kid.add_candy(candy)
160 else:
161 break
162
163 # Mark hosts for removal from the participants
164 if len(is_visited_by_all_kids[host]) == len(kids):
165 hosts_to_remove.append(host)
166
167 for host in hosts_to_remove:
168 hosts.remove(host)
169 is_visited_by_all_kids.pop(host)
170
171
172def calculate_distance(kid, host):
173 """Calculates the distance between two participants of the event."""
174 x1, y1 = kid.get_position()
175 x2, y2 = host.get_position()
176 return ((x2-x1)**2 + (y2-y1)**2)**(1/2)
177
178
179def determine_better_host(host, other_host):
180 """
181 If two hosts are equally distanced from a kid, returns the one with
182 lower x coordinate or else the one with lower y coordinate.
183 """
184 if host.get_position()[0] < other_host.get_position()[0]:
185 return host
186 if host.get_position()[0] > other_host.get_position()[0]:
187 return other_host
188 if host.get_position()[1] < other_host.get_position()[1]:
189 return host
190 return other_host
191
192
193def determine_max_mass(candies):
194 """Returns the heaviest candy, measured by mass(not only uranium)."""
195 return max(candies, key=lambda candy: candy.get_mass())
............
----------------------------------------------------------------------
Ran 12 tests in 0.001s
OK
f | 1 | """ | f | 1 | """ |
2 | Solution to homework 3 for Introduction to Python course at FMI | 2 | Solution to homework 3 for Introduction to Python course at FMI | ||
3 | by Nikolay Nikolaev. | 3 | by Nikolay Nikolaev. | ||
4 | """ | 4 | """ | ||
5 | import sys | 5 | import sys | ||
6 | 6 | ||||
7 | 7 | ||||
8 | class Candy: | 8 | class Candy: | ||
9 | """ | 9 | """ | ||
10 | Defines candy objects that have mass and uranium percentage. The two | 10 | Defines candy objects that have mass and uranium percentage. The two | ||
11 | methods are getters for the class values. | 11 | methods are getters for the class values. | ||
12 | """ | 12 | """ | ||
13 | def __init__(self, mass, uranium): | 13 | def __init__(self, mass, uranium): | ||
14 | self.mass = mass | 14 | self.mass = mass | ||
15 | self.uranium = uranium | 15 | self.uranium = uranium | ||
16 | 16 | ||||
17 | def get_uranium_quantity(self): | 17 | def get_uranium_quantity(self): | ||
18 | """Returns the mass of the uranium in the candy.""" | 18 | """Returns the mass of the uranium in the candy.""" | ||
19 | return self.mass * self.uranium | 19 | return self.mass * self.uranium | ||
20 | 20 | ||||
21 | def get_mass(self): | 21 | def get_mass(self): | ||
22 | """Returns the mass of the candy.""" | 22 | """Returns the mass of the candy.""" | ||
23 | return self.mass | 23 | return self.mass | ||
24 | 24 | ||||
25 | 25 | ||||
26 | class Person: | 26 | class Person: | ||
27 | """ | 27 | """ | ||
28 | Defines person object who is positioned in position. The two | 28 | Defines person object who is positioned in position. The two | ||
29 | methods are a setter and a getter for the position. | 29 | methods are a setter and a getter for the position. | ||
30 | """ | 30 | """ | ||
31 | def __init__(self, position): | 31 | def __init__(self, position): | ||
32 | self.position = position | 32 | self.position = position | ||
33 | 33 | ||||
34 | def get_position(self): | 34 | def get_position(self): | ||
35 | """"Returns the position of the person.""" | 35 | """"Returns the position of the person.""" | ||
36 | return self.position | 36 | return self.position | ||
37 | 37 | ||||
38 | def set_position(self, position): | 38 | def set_position(self, position): | ||
39 | """"Sets the position of the person.""" | 39 | """"Sets the position of the person.""" | ||
40 | self.position = position | 40 | self.position = position | ||
41 | 41 | ||||
42 | 42 | ||||
43 | class Kid(Person): | 43 | class Kid(Person): | ||
44 | """ | 44 | """ | ||
45 | Defines kid object that is inherited from Person. The initiative | 45 | Defines kid object that is inherited from Person. The initiative | ||
46 | attribute is used for sorting purposes of the kids. They can add | 46 | attribute is used for sorting purposes of the kids. They can add | ||
47 | candy objects in their baskets. | 47 | candy objects in their baskets. | ||
48 | """ | 48 | """ | ||
49 | def __init__(self, position, initiative): | 49 | def __init__(self, position, initiative): | ||
50 | super().__init__(position) | 50 | super().__init__(position) | ||
51 | self.initiative = initiative | 51 | self.initiative = initiative | ||
52 | self.candy_basket = [] | 52 | self.candy_basket = [] | ||
53 | 53 | ||||
54 | def get_initiative(self): | 54 | def get_initiative(self): | ||
55 | """Returns the initiative of the kid.""" | 55 | """Returns the initiative of the kid.""" | ||
56 | return self.initiative | 56 | return self.initiative | ||
57 | 57 | ||||
58 | def add_candy(self, candy): | 58 | def add_candy(self, candy): | ||
59 | """Adds a Candy object to the candy_basket of the kid.""" | 59 | """Adds a Candy object to the candy_basket of the kid.""" | ||
60 | self.candy_basket.append(candy) | 60 | self.candy_basket.append(candy) | ||
61 | 61 | ||||
62 | def is_critical(self): | 62 | def is_critical(self): | ||
63 | """ | 63 | """ | ||
64 | Return True if the mass of the uranium in the basket is | 64 | Return True if the mass of the uranium in the basket is | ||
65 | bigger than 20 grams. | 65 | bigger than 20 grams. | ||
66 | """ | 66 | """ | ||
67 | uranium_quantity = 0 | 67 | uranium_quantity = 0 | ||
68 | for candy in self.candy_basket: | 68 | for candy in self.candy_basket: | ||
69 | uranium_quantity += candy.get_uranium_quantity() | 69 | uranium_quantity += candy.get_uranium_quantity() | ||
70 | return uranium_quantity > 20 | 70 | return uranium_quantity > 20 | ||
71 | 71 | ||||
72 | 72 | ||||
73 | class Host(Person): | 73 | class Host(Person): | ||
74 | """ | 74 | """ | ||
75 | Defines host objects that are inherited from Person. They hold a | 75 | Defines host objects that are inherited from Person. They hold a | ||
76 | basket of Candy objects which they can remove. | 76 | basket of Candy objects which they can remove. | ||
77 | """ | 77 | """ | ||
78 | def __init__(self, position, candies): | 78 | def __init__(self, position, candies): | ||
79 | super().__init__(position) | 79 | super().__init__(position) | ||
80 | self.candy_basket = [Candy(mass, uranium) for mass, uranium in candies] | 80 | self.candy_basket = [Candy(mass, uranium) for mass, uranium in candies] | ||
81 | 81 | ||||
82 | def remove_candy(self, determining_function): | 82 | def remove_candy(self, determining_function): | ||
83 | """Removes a candy from the host's candy basket and retruns it.""" | 83 | """Removes a candy from the host's candy basket and retruns it.""" | ||
84 | if self.candy_basket: | 84 | if self.candy_basket: | ||
85 | chosen_candy = determining_function(self.candy_basket) | 85 | chosen_candy = determining_function(self.candy_basket) | ||
86 | self.candy_basket.remove(chosen_candy) | 86 | self.candy_basket.remove(chosen_candy) | ||
87 | return chosen_candy | 87 | return chosen_candy | ||
88 | return None | 88 | return None | ||
89 | 89 | ||||
n | 90 | def is_candy_basket_empty(self): | n | ||
91 | """Returns True when the host's candy basket is empty.""" | ||||
92 | return not self.candy_basket | ||||
93 | |||||
94 | 90 | ||||
95 | class FluxCapacitor: | 91 | class FluxCapacitor: | ||
96 | """ | 92 | """ | ||
97 | Organises a scenario where the participants are kids and hosts. The kids | 93 | Organises a scenario where the participants are kids and hosts. The kids | ||
98 | collect candies from hosts. Whenever a kid/kids collect/s a critical mass | 94 | collect candies from hosts. Whenever a kid/kids collect/s a critical mass | ||
99 | of uranium, all the hosts are visited by all the kids, or all candies of | 95 | of uranium, all the hosts are visited by all the kids, or all candies of | ||
100 | the hosts baskets are collected - the simulation stops. In the first case | 96 | the hosts baskets are collected - the simulation stops. In the first case | ||
101 | the victims are returned and in the other two - None. | 97 | the victims are returned and in the other two - None. | ||
102 | """ | 98 | """ | ||
103 | def __init__(self, participants): | 99 | def __init__(self, participants): | ||
104 | self.participants = participants | 100 | self.participants = participants | ||
105 | 101 | ||||
106 | def get_victim(self): | 102 | def get_victim(self): | ||
107 | """ | 103 | """ | ||
108 | Simulates the events of kids collecting candies of hosts. Returns | 104 | Simulates the events of kids collecting candies of hosts. Returns | ||
109 | the victims if there are any. Otherwise, returns None. | 105 | the victims if there are any. Otherwise, returns None. | ||
110 | """ | 106 | """ | ||
111 | kids = {kid for kid in self.participants if isinstance(kid, Kid)} | 107 | kids = {kid for kid in self.participants if isinstance(kid, Kid)} | ||
112 | hosts = {host for host in self.participants if isinstance(host, Host)} | 108 | hosts = {host for host in self.participants if isinstance(host, Host)} | ||
113 | is_visited_by_all_kids = {host:[] for host in hosts} | 109 | is_visited_by_all_kids = {host:[] for host in hosts} | ||
114 | 110 | ||||
115 | while True: | 111 | while True: | ||
116 | victims = set() | 112 | victims = set() | ||
117 | 113 | ||||
118 | for kid in kids: | 114 | for kid in kids: | ||
119 | if kid.is_critical(): | 115 | if kid.is_critical(): | ||
120 | victims.add(kid) | 116 | victims.add(kid) | ||
121 | 117 | ||||
122 | # If there is at least one victim, return it | 118 | # If there is at least one victim, return it | ||
123 | if victims: | 119 | if victims: | ||
124 | return victims | 120 | return victims | ||
125 | 121 | ||||
126 | # If all hosts don't have candies or were | 122 | # If all hosts don't have candies or were | ||
127 | # visited by all kids, return None | 123 | # visited by all kids, return None | ||
128 | if not hosts: | 124 | if not hosts: | ||
129 | return None | 125 | return None | ||
130 | 126 | ||||
131 | # The variable current_visitors stores all the | 127 | # The variable current_visitors stores all the | ||
132 | # kids that are at each host at the current iteration. | 128 | # kids that are at each host at the current iteration. | ||
133 | current_visitors = {host:[] for host in hosts} | 129 | current_visitors = {host:[] for host in hosts} | ||
134 | for kid in kids: | 130 | for kid in kids: | ||
135 | minimum_distance = sys.float_info.max | 131 | minimum_distance = sys.float_info.max | ||
136 | next_host = None | 132 | next_host = None | ||
137 | 133 | ||||
138 | # Finds the nearest host for each kid without repetition | 134 | # Finds the nearest host for each kid without repetition | ||
139 | for host in hosts: | 135 | for host in hosts: | ||
140 | if kid not in is_visited_by_all_kids[host]: | 136 | if kid not in is_visited_by_all_kids[host]: | ||
141 | distance = calculate_distance(kid, host) | 137 | distance = calculate_distance(kid, host) | ||
142 | if distance == minimum_distance: | 138 | if distance == minimum_distance: | ||
143 | next_host = determine_better_host(host, next_host) | 139 | next_host = determine_better_host(host, next_host) | ||
144 | if distance < minimum_distance: | 140 | if distance < minimum_distance: | ||
145 | minimum_distance = distance | 141 | minimum_distance = distance | ||
146 | next_host = host | 142 | next_host = host | ||
147 | 143 | ||||
148 | if next_host is not None: | 144 | if next_host is not None: | ||
149 | current_visitors[next_host].append(kid) | 145 | current_visitors[next_host].append(kid) | ||
150 | kid.set_position(next_host.get_position()) | 146 | kid.set_position(next_host.get_position()) | ||
151 | 147 | ||||
152 | hosts_to_remove = [] | 148 | hosts_to_remove = [] | ||
153 | for host in hosts: | 149 | for host in hosts: | ||
154 | # Sort kids by their initiative | 150 | # Sort kids by their initiative | ||
155 | current_visitors[host] = sorted(current_visitors[host], | 151 | current_visitors[host] = sorted(current_visitors[host], | ||
156 | key=lambda kid: kid.get_initiative(), reverse = True) | 152 | key=lambda kid: kid.get_initiative(), reverse = True) | ||
157 | 153 | ||||
158 | is_visited_by_all_kids[host] += current_visitors[host] | 154 | is_visited_by_all_kids[host] += current_visitors[host] | ||
159 | # and give them candy. | 155 | # and give them candy. | ||
160 | for kid in current_visitors[host]: | 156 | for kid in current_visitors[host]: | ||
161 | candy = host.remove_candy(determine_max_mass) | 157 | candy = host.remove_candy(determine_max_mass) | ||
162 | if candy: | 158 | if candy: | ||
163 | kid.add_candy(candy) | 159 | kid.add_candy(candy) | ||
164 | else: | 160 | else: | ||
165 | break | 161 | break | ||
166 | 162 | ||||
167 | # Mark hosts for removal from the participants | 163 | # Mark hosts for removal from the participants | ||
t | 168 | if (host.is_candy_basket_empty() or | t | ||
169 | len(is_visited_by_all_kids[host]) == len(kids)): | 164 | if len(is_visited_by_all_kids[host]) == len(kids): | ||
170 | hosts_to_remove.append(host) | 165 | hosts_to_remove.append(host) | ||
171 | 166 | ||||
172 | for host in hosts_to_remove: | 167 | for host in hosts_to_remove: | ||
173 | hosts.remove(host) | 168 | hosts.remove(host) | ||
174 | is_visited_by_all_kids.pop(host) | 169 | is_visited_by_all_kids.pop(host) | ||
175 | 170 | ||||
176 | 171 | ||||
177 | def calculate_distance(kid, host): | 172 | def calculate_distance(kid, host): | ||
178 | """Calculates the distance between two participants of the event.""" | 173 | """Calculates the distance between two participants of the event.""" | ||
179 | x1, y1 = kid.get_position() | 174 | x1, y1 = kid.get_position() | ||
180 | x2, y2 = host.get_position() | 175 | x2, y2 = host.get_position() | ||
181 | return ((x2-x1)**2 + (y2-y1)**2)**(1/2) | 176 | return ((x2-x1)**2 + (y2-y1)**2)**(1/2) | ||
182 | 177 | ||||
183 | 178 | ||||
184 | def determine_better_host(host, other_host): | 179 | def determine_better_host(host, other_host): | ||
185 | """ | 180 | """ | ||
186 | If two hosts are equally distanced from a kid, returns the one with | 181 | If two hosts are equally distanced from a kid, returns the one with | ||
187 | lower x coordinate or else the one with lower y coordinate. | 182 | lower x coordinate or else the one with lower y coordinate. | ||
188 | """ | 183 | """ | ||
189 | if host.get_position()[0] < other_host.get_position()[0]: | 184 | if host.get_position()[0] < other_host.get_position()[0]: | ||
190 | return host | 185 | return host | ||
191 | if host.get_position()[0] > other_host.get_position()[0]: | 186 | if host.get_position()[0] > other_host.get_position()[0]: | ||
192 | return other_host | 187 | return other_host | ||
193 | if host.get_position()[1] < other_host.get_position()[1]: | 188 | if host.get_position()[1] < other_host.get_position()[1]: | ||
194 | return host | 189 | return host | ||
195 | return other_host | 190 | return other_host | ||
196 | 191 | ||||
197 | 192 | ||||
198 | def determine_max_mass(candies): | 193 | def determine_max_mass(candies): | ||
199 | """Returns the heaviest candy, measured by mass(not only uranium).""" | 194 | """Returns the heaviest candy, measured by mass(not only uranium).""" | ||
200 | return max(candies, key=lambda candy: candy.get_mass()) | 195 | return max(candies, key=lambda candy: candy.get_mass()) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | """ | f | 1 | """ |
2 | Solution to homework 3 for Introduction to Python course at FMI | 2 | Solution to homework 3 for Introduction to Python course at FMI | ||
3 | by Nikolay Nikolaev. | 3 | by Nikolay Nikolaev. | ||
4 | """ | 4 | """ | ||
5 | import sys | 5 | import sys | ||
6 | 6 | ||||
n | n | 7 | |||
7 | class Candy: | 8 | class Candy: | ||
8 | """ | 9 | """ | ||
9 | Defines candy objects that have mass and uranium percentage. The two | 10 | Defines candy objects that have mass and uranium percentage. The two | ||
10 | methods are getters for the class values. | 11 | methods are getters for the class values. | ||
11 | """ | 12 | """ | ||
12 | def __init__(self, mass, uranium): | 13 | def __init__(self, mass, uranium): | ||
13 | self.mass = mass | 14 | self.mass = mass | ||
14 | self.uranium = uranium | 15 | self.uranium = uranium | ||
15 | 16 | ||||
16 | def get_uranium_quantity(self): | 17 | def get_uranium_quantity(self): | ||
17 | """Returns the mass of the uranium in the candy.""" | 18 | """Returns the mass of the uranium in the candy.""" | ||
18 | return self.mass * self.uranium | 19 | return self.mass * self.uranium | ||
19 | 20 | ||||
20 | def get_mass(self): | 21 | def get_mass(self): | ||
21 | """Returns the mass of the candy.""" | 22 | """Returns the mass of the candy.""" | ||
22 | return self.mass | 23 | return self.mass | ||
23 | 24 | ||||
24 | 25 | ||||
25 | class Person: | 26 | class Person: | ||
26 | """ | 27 | """ | ||
27 | Defines person object who is positioned in position. The two | 28 | Defines person object who is positioned in position. The two | ||
28 | methods are a setter and a getter for the position. | 29 | methods are a setter and a getter for the position. | ||
29 | """ | 30 | """ | ||
30 | def __init__(self, position): | 31 | def __init__(self, position): | ||
31 | self.position = position | 32 | self.position = position | ||
32 | 33 | ||||
33 | def get_position(self): | 34 | def get_position(self): | ||
34 | """"Returns the position of the person.""" | 35 | """"Returns the position of the person.""" | ||
35 | return self.position | 36 | return self.position | ||
36 | 37 | ||||
37 | def set_position(self, position): | 38 | def set_position(self, position): | ||
38 | """"Sets the position of the person.""" | 39 | """"Sets the position of the person.""" | ||
39 | self.position = position | 40 | self.position = position | ||
40 | 41 | ||||
41 | 42 | ||||
42 | class Kid(Person): | 43 | class Kid(Person): | ||
43 | """ | 44 | """ | ||
44 | Defines kid object that is inherited from Person. The initiative | 45 | Defines kid object that is inherited from Person. The initiative | ||
45 | attribute is used for sorting purposes of the kids. They can add | 46 | attribute is used for sorting purposes of the kids. They can add | ||
n | 46 | candy objects in their baskets. If the mass of the uranium in the | n | 47 | candy objects in their baskets. |
47 | basket is bigger than 20 grams it is critical mass. | ||||
48 | """ | 48 | """ | ||
49 | def __init__(self, position, initiative): | 49 | def __init__(self, position, initiative): | ||
50 | super().__init__(position) | 50 | super().__init__(position) | ||
51 | self.initiative = initiative | 51 | self.initiative = initiative | ||
52 | self.candy_basket = [] | 52 | self.candy_basket = [] | ||
53 | 53 | ||||
54 | def get_initiative(self): | 54 | def get_initiative(self): | ||
55 | """Returns the initiative of the kid.""" | 55 | """Returns the initiative of the kid.""" | ||
56 | return self.initiative | 56 | return self.initiative | ||
57 | 57 | ||||
58 | def add_candy(self, candy): | 58 | def add_candy(self, candy): | ||
59 | """Adds a Candy object to the candy_basket of the kid.""" | 59 | """Adds a Candy object to the candy_basket of the kid.""" | ||
60 | self.candy_basket.append(candy) | 60 | self.candy_basket.append(candy) | ||
61 | 61 | ||||
62 | def is_critical(self): | 62 | def is_critical(self): | ||
63 | """ | 63 | """ | ||
n | 64 | Is True when the mass of the uranium in the basket of kid | n | 64 | Return True if the mass of the uranium in the basket is |
65 | is heavier than 20 grams. | 65 | bigger than 20 grams. | ||
66 | """ | 66 | """ | ||
67 | uranium_quantity = 0 | 67 | uranium_quantity = 0 | ||
68 | for candy in self.candy_basket: | 68 | for candy in self.candy_basket: | ||
69 | uranium_quantity += candy.get_uranium_quantity() | 69 | uranium_quantity += candy.get_uranium_quantity() | ||
70 | return uranium_quantity > 20 | 70 | return uranium_quantity > 20 | ||
71 | 71 | ||||
72 | 72 | ||||
73 | class Host(Person): | 73 | class Host(Person): | ||
74 | """ | 74 | """ | ||
75 | Defines host objects that are inherited from Person. They hold a | 75 | Defines host objects that are inherited from Person. They hold a | ||
n | 76 | basket of Candy objects which they can remove, based on a function, | n | 76 | basket of Candy objects which they can remove. |
77 | determining the first removed candy. It has a method that returns a | ||||
78 | boolean value if the candy basket is empty or not. | ||||
79 | """ | 77 | """ | ||
80 | def __init__(self, position, candies): | 78 | def __init__(self, position, candies): | ||
81 | super().__init__(position) | 79 | super().__init__(position) | ||
82 | self.candy_basket = [Candy(mass, uranium) for mass, uranium in candies] | 80 | self.candy_basket = [Candy(mass, uranium) for mass, uranium in candies] | ||
83 | 81 | ||||
84 | def remove_candy(self, determining_function): | 82 | def remove_candy(self, determining_function): | ||
n | 85 | """"Removes a candy from the host's candy basket and retruns it.""" | n | 83 | """Removes a candy from the host's candy basket and retruns it.""" |
86 | if self.candy_basket: | 84 | if self.candy_basket: | ||
87 | chosen_candy = determining_function(self.candy_basket) | 85 | chosen_candy = determining_function(self.candy_basket) | ||
88 | self.candy_basket.remove(chosen_candy) | 86 | self.candy_basket.remove(chosen_candy) | ||
89 | return chosen_candy | 87 | return chosen_candy | ||
90 | return None | 88 | return None | ||
91 | 89 | ||||
92 | def is_candy_basket_empty(self): | 90 | def is_candy_basket_empty(self): | ||
93 | """Returns True when the host's candy basket is empty.""" | 91 | """Returns True when the host's candy basket is empty.""" | ||
n | 94 | if self.candy_basket: | n | 92 | return not self.candy_basket |
95 | return False | ||||
96 | return True | ||||
97 | 93 | ||||
98 | 94 | ||||
99 | class FluxCapacitor: | 95 | class FluxCapacitor: | ||
100 | """ | 96 | """ | ||
101 | Organises a scenario where the participants are kids and hosts. The kids | 97 | Organises a scenario where the participants are kids and hosts. The kids | ||
102 | collect candies from hosts. Whenever a kid/kids collect/s a critical mass | 98 | collect candies from hosts. Whenever a kid/kids collect/s a critical mass | ||
103 | of uranium, all the hosts are visited by all the kids, or all candies of | 99 | of uranium, all the hosts are visited by all the kids, or all candies of | ||
104 | the hosts baskets are collected - the simulation stops. In the first case | 100 | the hosts baskets are collected - the simulation stops. In the first case | ||
105 | the victims are returned and in the other two - None. | 101 | the victims are returned and in the other two - None. | ||
106 | """ | 102 | """ | ||
107 | def __init__(self, participants): | 103 | def __init__(self, participants): | ||
108 | self.participants = participants | 104 | self.participants = participants | ||
109 | 105 | ||||
110 | def get_victim(self): | 106 | def get_victim(self): | ||
n | 111 | """" | n | 107 | """ |
112 | Simulates the events of kids collecting candies of hosts. Returns | 108 | Simulates the events of kids collecting candies of hosts. Returns | ||
113 | the victims if there are any. Otherwise, returns None. | 109 | the victims if there are any. Otherwise, returns None. | ||
114 | """ | 110 | """ | ||
115 | kids = {kid for kid in self.participants if isinstance(kid, Kid)} | 111 | kids = {kid for kid in self.participants if isinstance(kid, Kid)} | ||
116 | hosts = {host for host in self.participants if isinstance(host, Host)} | 112 | hosts = {host for host in self.participants if isinstance(host, Host)} | ||
n | n | 113 | is_visited_by_all_kids = {host:[] for host in hosts} | ||
117 | 114 | ||||
118 | while True: | 115 | while True: | ||
119 | victims = set() | 116 | victims = set() | ||
n | 120 | is_visited_by_all_kids = {host:[] for host in hosts} | n | ||
121 | 117 | ||||
122 | for kid in kids: | 118 | for kid in kids: | ||
123 | if kid.is_critical(): | 119 | if kid.is_critical(): | ||
124 | victims.add(kid) | 120 | victims.add(kid) | ||
125 | 121 | ||||
126 | # If there is at least one victim, return it | 122 | # If there is at least one victim, return it | ||
127 | if victims: | 123 | if victims: | ||
128 | return victims | 124 | return victims | ||
129 | 125 | ||||
130 | # If all hosts don't have candies or were | 126 | # If all hosts don't have candies or were | ||
131 | # visited by all kids, return None | 127 | # visited by all kids, return None | ||
132 | if not hosts: | 128 | if not hosts: | ||
133 | return None | 129 | return None | ||
134 | 130 | ||||
135 | # The variable current_visitors stores all the | 131 | # The variable current_visitors stores all the | ||
136 | # kids that are at each host at the current iteration. | 132 | # kids that are at each host at the current iteration. | ||
137 | current_visitors = {host:[] for host in hosts} | 133 | current_visitors = {host:[] for host in hosts} | ||
138 | for kid in kids: | 134 | for kid in kids: | ||
139 | minimum_distance = sys.float_info.max | 135 | minimum_distance = sys.float_info.max | ||
140 | next_host = None | 136 | next_host = None | ||
141 | 137 | ||||
142 | # Finds the nearest host for each kid without repetition | 138 | # Finds the nearest host for each kid without repetition | ||
143 | for host in hosts: | 139 | for host in hosts: | ||
144 | if kid not in is_visited_by_all_kids[host]: | 140 | if kid not in is_visited_by_all_kids[host]: | ||
145 | distance = calculate_distance(kid, host) | 141 | distance = calculate_distance(kid, host) | ||
146 | if distance == minimum_distance: | 142 | if distance == minimum_distance: | ||
147 | next_host = determine_better_host(host, next_host) | 143 | next_host = determine_better_host(host, next_host) | ||
148 | if distance < minimum_distance: | 144 | if distance < minimum_distance: | ||
149 | minimum_distance = distance | 145 | minimum_distance = distance | ||
150 | next_host = host | 146 | next_host = host | ||
151 | 147 | ||||
n | n | 148 | if next_host is not None: | ||
152 | current_visitors[next_host].append(kid) | 149 | current_visitors[next_host].append(kid) | ||
153 | kid.set_position(next_host.get_position()) | 150 | kid.set_position(next_host.get_position()) | ||
154 | 151 | ||||
155 | hosts_to_remove = [] | 152 | hosts_to_remove = [] | ||
156 | for host in hosts: | 153 | for host in hosts: | ||
157 | # Sort kids by their initiative | 154 | # Sort kids by their initiative | ||
158 | current_visitors[host] = sorted(current_visitors[host], | 155 | current_visitors[host] = sorted(current_visitors[host], | ||
159 | key=lambda kid: kid.get_initiative(), reverse = True) | 156 | key=lambda kid: kid.get_initiative(), reverse = True) | ||
160 | 157 | ||||
161 | is_visited_by_all_kids[host] += current_visitors[host] | 158 | is_visited_by_all_kids[host] += current_visitors[host] | ||
162 | # and give them candy. | 159 | # and give them candy. | ||
163 | for kid in current_visitors[host]: | 160 | for kid in current_visitors[host]: | ||
164 | candy = host.remove_candy(determine_max_mass) | 161 | candy = host.remove_candy(determine_max_mass) | ||
165 | if candy: | 162 | if candy: | ||
166 | kid.add_candy(candy) | 163 | kid.add_candy(candy) | ||
167 | else: | 164 | else: | ||
168 | break | 165 | break | ||
169 | 166 | ||||
170 | # Mark hosts for removal from the participants | 167 | # Mark hosts for removal from the participants | ||
171 | if (host.is_candy_basket_empty() or | 168 | if (host.is_candy_basket_empty() or | ||
172 | len(is_visited_by_all_kids[host]) == len(kids)): | 169 | len(is_visited_by_all_kids[host]) == len(kids)): | ||
173 | hosts_to_remove.append(host) | 170 | hosts_to_remove.append(host) | ||
174 | 171 | ||||
175 | for host in hosts_to_remove: | 172 | for host in hosts_to_remove: | ||
176 | hosts.remove(host) | 173 | hosts.remove(host) | ||
177 | is_visited_by_all_kids.pop(host) | 174 | is_visited_by_all_kids.pop(host) | ||
178 | 175 | ||||
179 | 176 | ||||
180 | def calculate_distance(kid, host): | 177 | def calculate_distance(kid, host): | ||
181 | """Calculates the distance between two participants of the event.""" | 178 | """Calculates the distance between two participants of the event.""" | ||
182 | x1, y1 = kid.get_position() | 179 | x1, y1 = kid.get_position() | ||
183 | x2, y2 = host.get_position() | 180 | x2, y2 = host.get_position() | ||
184 | return ((x2-x1)**2 + (y2-y1)**2)**(1/2) | 181 | return ((x2-x1)**2 + (y2-y1)**2)**(1/2) | ||
185 | 182 | ||||
186 | 183 | ||||
187 | def determine_better_host(host, other_host): | 184 | def determine_better_host(host, other_host): | ||
188 | """ | 185 | """ | ||
189 | If two hosts are equally distanced from a kid, returns the one with | 186 | If two hosts are equally distanced from a kid, returns the one with | ||
190 | lower x coordinate or else the one with lower y coordinate. | 187 | lower x coordinate or else the one with lower y coordinate. | ||
191 | """ | 188 | """ | ||
192 | if host.get_position()[0] < other_host.get_position()[0]: | 189 | if host.get_position()[0] < other_host.get_position()[0]: | ||
193 | return host | 190 | return host | ||
194 | if host.get_position()[0] > other_host.get_position()[0]: | 191 | if host.get_position()[0] > other_host.get_position()[0]: | ||
195 | return other_host | 192 | return other_host | ||
196 | if host.get_position()[1] < other_host.get_position()[1]: | 193 | if host.get_position()[1] < other_host.get_position()[1]: | ||
197 | return host | 194 | return host | ||
198 | return other_host | 195 | return other_host | ||
199 | 196 | ||||
t | t | 197 | |||
200 | def determine_max_mass(candies): | 198 | def determine_max_mass(candies): | ||
201 | """Returns the heaviest candy, measured by mass(not only uranium).""" | 199 | """Returns the heaviest candy, measured by mass(not only uranium).""" | ||
202 | return max(candies, key=lambda candy: candy.get_mass()) | 200 | return max(candies, key=lambda candy: candy.get_mass()) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|