f | import math | f | import math |
n | | n | |
| BIG_NUM = 100000 | | |
| | | |
| | | |
| def choose_candy_from_host(candy_bag): | | |
| """Get the chunkiest candy from the host's bag. Candies in the bag are previously sorted by mass.""" | | |
| return candy_bag[-1] | | |
| | | |
| | | |
| class Candy: | | class Candy: |
n | | n | |
| def __init__(self, mass, uranium): | | def __init__(self, mass, uranium): |
n | """Constructor.""" | n | """Initializer.""" |
| self._mass = mass | | self._mass = mass |
| self._uranium = uranium | | self._uranium = uranium |
| | | |
| def get_uranium_quantity(self): | | def get_uranium_quantity(self): |
| """Calculate and return the quantity of uranium in grams.""" | | """Calculate and return the quantity of uranium in grams.""" |
| return self._mass * self._uranium | | return self._mass * self._uranium |
| | | |
| def get_mass(self): | | def get_mass(self): |
| """Return the mass of the candy.""" | | """Return the mass of the candy.""" |
| return self._mass | | return self._mass |
| | | |
| | | |
| class Person: | | class Person: |
n | | n | |
| def __init__(self, position): | | def __init__(self, position): |
n | """Constructor.""" | n | """Initializer.""" |
| self._position = position | | self._position = position |
| | | |
| def get_position(self): | | def get_position(self): |
n | """Return the position (coordinates) of the person as (x, y) tuple.""" | n | """Return the position (coordinates) of the person.""" |
| return self._position | | return self._position |
| | | |
| def set_position(self, new_position): | | def set_position(self, new_position): |
| """Change the position to the new coordinates: new_position.""" | | """Change the position to the new coordinates: new_position.""" |
| self._position = new_position | | self._position = new_position |
| | | |
| | | |
| class Kid(Person): | | class Kid(Person): |
n | | n | |
| | | CRITICAL_RADIATION = 20 |
| | | |
| def __init__(self, position, initiative): | | def __init__(self, position, initiative): |
n | """Constructor.""" | n | """Initializer.""" |
| super().__init__(position) | | super().__init__(position) |
| self._initiative = initiative | | self._initiative = initiative |
| self._current_radiation = 0 | | self._current_radiation = 0 |
n | self._critical_radiation = 20 | n | self._candy_bag = [] |
| self.visited_hosts = [] | | self.visited_hosts = [] |
| | | |
| def get_initiative(self): | | def get_initiative(self): |
| """Return the value of the initiative.""" | | """Return the value of the initiative.""" |
| return self._initiative | | return self._initiative |
| | | |
| def add_candy(self, candy): | | def add_candy(self, candy): |
| """Get Candy object instance and add its radiation to the total current radiation sum.""" | | """Get Candy object instance and add its radiation to the total current radiation sum.""" |
| self._current_radiation += candy.get_uranium_quantity() | | self._current_radiation += candy.get_uranium_quantity() |
n | | n | self._candy_bag.append(candy) |
| | | |
| def is_critical(self): | | def is_critical(self): |
| """Return bool value of whether the accumulated candy radiation has reached critical amounts.""" | | """Return bool value of whether the accumulated candy radiation has reached critical amounts.""" |
n | return self._current_radiation > self._critical_radiation | n | return self._current_radiation > self.CRITICAL_RADIATION |
| | | |
| | | |
| class Host(Person): | | class Host(Person): |
n | | n | |
| def __init__(self, position, candies): | | def __init__(self, position, candies): |
n | """Constructor.""" | n | """Initializer.""" |
| super().__init__(position) | | super().__init__(position) |
n | self.candy_bag = [Candy(candy[0], candy[1]) for candy in candies] | n | self.candy_bag = [Candy(*candy) for candy in candies] |
| self.sort_candy_bag_based_on_mass() | | self.sort_candy_bag_based_on_mass() |
| | | |
| def sort_candy_bag_based_on_mass(self): | | def sort_candy_bag_based_on_mass(self): |
| """Sort the candy bag so that candies go from the lowest mass to the highest mass.""" | | """Sort the candy bag so that candies go from the lowest mass to the highest mass.""" |
| self.candy_bag.sort(key=lambda candy: candy.get_mass()) | | self.candy_bag.sort(key=lambda candy: candy.get_mass()) |
| | | |
| def remove_candy(self, choose_candy_to_give): | | def remove_candy(self, choose_candy_to_give): |
| """Remove a candy from the candy bag. Which one is determined by the passed argument - function.""" | | """Remove a candy from the candy bag. Which one is determined by the passed argument - function.""" |
| if self.candy_bag: | | if self.candy_bag: |
| candy_to_remove = choose_candy_to_give(self.candy_bag) | | candy_to_remove = choose_candy_to_give(self.candy_bag) |
n | if candy_to_remove in self.candy_bag: | n | |
| self.candy_bag.remove(candy_to_remove) | | self.candy_bag.remove(candy_to_remove) |
| return candy_to_remove | | return candy_to_remove |
| return None | | return None |
| | | |
| | | |
| class FluxCapacitor: | | class FluxCapacitor: |
n | | n | |
| def __init__(self, participants): | | def __init__(self, participants): |
n | """Constructor.""" | n | """Initializer.""" |
| self._participants = participants | | self._participants = participants |
| self._victims = set() | | self._victims = set() |
| self._kids = self._get_kids() | | self._kids = self._get_kids() |
| self._hosts = self._get_hosts() | | self._hosts = self._get_hosts() |
| | | |
| def _get_kids(self): | | def _get_kids(self): |
| """Return set of kids.""" | | """Return set of kids.""" |
| return {kid for kid in self._participants if isinstance(kid, Kid)} | | return {kid for kid in self._participants if isinstance(kid, Kid)} |
| | | |
| def _get_hosts(self): | | def _get_hosts(self): |
| """Return set of hosts.""" | | """Return set of hosts.""" |
| return {host for host in self._participants if isinstance(host, Host)} | | return {host for host in self._participants if isinstance(host, Host)} |
| | | |
n | @staticmethod | n | |
| def _is_closer(last_closest_host, curr_host, kid): | | |
| """Return bool if the current host is closer to the current child than the last host.""" | | |
| curr_dist = math.dist(kid.get_position(), curr_host.get_position()) | | |
| last_shortest = math.dist(last_closest_host.get_position(), kid.get_position()) if last_closest_host else BIG_NUM | | |
| | | |
| if curr_dist < last_shortest: | | |
| return True | | |
| if math.isclose(curr_dist, last_shortest): | | |
| if curr_host.get_position()[0] < last_closest_host.get_position()[0]: | | |
| return True | | |
| elif (math.isclose(curr_host.get_position()[0], last_closest_host.get_position()[0]) and | | |
| curr_host.get_position()[1] < last_closest_host.get_position()[1]): | | |
| return True | | |
| return False | | |
| | | |
| def _get_next_host_to_visit(self, kid): | | def _get_next_host_to_visit(self, kid): |
| """Find the next host for each left kid to visit based on shortest distance.""" | | """Find the next host for each left kid to visit based on shortest distance.""" |
n | last_closest_host = None | n | list_distances_hosts = [] |
| for curr_host in self._hosts: | | for curr_host in self._hosts: |
| if curr_host not in kid.visited_hosts: | | if curr_host not in kid.visited_hosts: |
n | if self._is_closer(last_closest_host, curr_host, kid): | n | curr_dist = math.dist(kid.get_position(), curr_host.get_position()) |
| last_closest_host = curr_host | | dist_x_y = (curr_dist, curr_host.get_position()[0], curr_host.get_position()[1], curr_host) |
| return last_closest_host | | list_distances_hosts.append(dist_x_y) |
| | | |
| | | list_distances_hosts.sort() |
| | | return list_distances_hosts[0][-1] |
| | | |
| def _get_kids_that_will_visit(self, host): | | def _get_kids_that_will_visit(self, host): |
| """Get list of kids that will visit the current host. Sort list based on initiative.""" | | """Get list of kids that will visit the current host. Sort list based on initiative.""" |
| if self._kids: | | if self._kids: |
| kids_to_visit = [] | | kids_to_visit = [] |
| for kid in self._kids: | | for kid in self._kids: |
| if kid not in self._victims: | | if kid not in self._victims: |
n | if self._get_next_host_to_visit(kid) == host: | n | if self._get_next_host_to_visit(kid) is host: |
| kids_to_visit.append(kid) | | kids_to_visit.append(kid) |
| kids_to_visit.sort(key=lambda curr_kid: curr_kid.get_initiative(), reverse=True) | | kids_to_visit.sort(key=lambda curr_kid: curr_kid.get_initiative(), reverse=True) |
| return kids_to_visit | | return kids_to_visit |
| return None | | return None |
n | | n | |
| | | @staticmethod |
| | | def choose_candy_from_host(candy_bag): |
| | | """Get the chunkiest candy from the host's bag. Candies in the bag are previously sorted by mass.""" |
| | | return candy_bag[-1] |
| | | |
| def _give_poisonous_candy(self, host): | | def _give_poisonous_candy(self, host): |
| """Attempt to give poisonous candy to the innocent children. If any kid dies, add it to the pile of victims.""" | | """Attempt to give poisonous candy to the innocent children. If any kid dies, add it to the pile of victims.""" |
| if self._get_kids_that_will_visit(host): | | if self._get_kids_that_will_visit(host): |
| visitation_happened = False | | visitation_happened = False |
| for kid in self._get_kids_that_will_visit(host): | | for kid in self._get_kids_that_will_visit(host): |
| if host.candy_bag: | | if host.candy_bag: |
| visitation_happened = True | | visitation_happened = True |
t | candy_to_be_given = host.remove_candy(choose_candy_from_host) | t | candy_to_be_given = host.remove_candy(self.choose_candy_from_host) |
| kid.add_candy(candy_to_be_given) | | kid.add_candy(candy_to_be_given) |
| kid.visited_hosts.append(host) | | kid.visited_hosts.append(host) |
| kid.set_position(host.get_position()) | | kid.set_position(host.get_position()) |
| if kid.is_critical(): | | if kid.is_critical(): |
| self._victims.add(kid) | | self._victims.add(kid) |
| return visitation_happened | | return visitation_happened |
| return False | | return False |
| | | |
| def get_victim(self): | | def get_victim(self): |
| """Iterate visitations to distribute the candy. Return set of the first victims or None if all survived.""" | | """Iterate visitations to distribute the candy. Return set of the first victims or None if all survived.""" |
| happy_ending = False | | happy_ending = False |
| while not self._victims and not happy_ending: | | while not self._victims and not happy_ending: |
| happy_ending = True | | happy_ending = True |
| for host in self._hosts: | | for host in self._hosts: |
| if self._give_poisonous_candy(host): | | if self._give_poisonous_candy(host): |
| # No happy ending until all the candy has been given | | # No happy ending until all the candy has been given |
| happy_ending = False | | happy_ending = False |
| return None if happy_ending else self._victims | | return None if happy_ending else self._victims |