Домашни > Хелоуин в Припят > Решения > Решението на Георги Кунчев

Резултати
10 точки от тестове
0 точки от учител

10 точки общо

12 успешни теста
0 неуспешни теста
Код (Reupload original working solution)

  1import math
  2
  3
  4class Candy:
  5    """Represent a Halloween in Pripyat candy."""
  6
  7    def __init__(self, mass, uranium):
  8        """Initializator."""
  9        self._mass = mass
 10        self._uranium = uranium
 11    
 12    def get_uranium_quantity(self):
 13        """Get absolute content of uranium."""
 14        return self._mass * self._uranium
 15    
 16    def get_mass(self):
 17        """Get the mass of the candy."""
 18        return self._mass
 19
 20
 21class Person:
 22    """Represent a Halloween in Pripyat person."""
 23
 24    def __init__(self, position):
 25        """Initializator."""
 26        self._position = position
 27
 28    def get_position(self):
 29        """Get the current position of a person."""
 30        return self._position
 31
 32    def set_position(self, position):
 33        """Set the current position of a person."""
 34        self._position = position
 35
 36
 37class Kid(Person):
 38    """Represent a Halloween in Pripyat kid."""
 39
 40    CRITICAL_URANIUM = 20
 41
 42    def __init__(self, position, initiative):
 43        """Initializator."""
 44        super().__init__(position)
 45        self._candies = set()
 46        self._initiative = initiative
 47
 48    def get_initiative(self):
 49        """Get the initiative value of the kid."""
 50        return self._initiative
 51
 52    def add_candy(self, candy):
 53        """Add a new candy to the set of owned candies."""
 54        self._candies.add(candy)
 55    
 56    def is_critical(self):
 57        """Check if the uranium mass in owned candies is critical."""
 58        total = sum([candy.get_uranium_quantity() for candy in self._candies])
 59        return total > self.CRITICAL_URANIUM
 60
 61
 62class Host(Person):
 63    """Represent a Halloween in Pripyat host."""
 64
 65    def __init__(self, position, candies):
 66        """Initializator."""
 67        super().__init__(position)
 68        self._candies = {Candy(*candy) for candy in candies}
 69
 70    def remove_candy(self, fun):
 71        """Remove a candy from the set of owned candies."""
 72        if not self._candies:
 73            return None
 74        candy = fun(self._candies)
 75        self._candies.remove(candy)
 76        return candy
 77
 78
 79class Visit:
 80    """A single visit between a kid and a host."""
 81
 82    def __init__(self, kid, host, turn_index):
 83        """Initializator."""
 84        self.kid = kid
 85        self.host = host
 86        self.turn_index = turn_index
 87
 88
 89class FluxCapacitor:
 90    """Represent a Halloween in Pripyat simulator."""
 91
 92    def __init__(self, participants):
 93        """Initializator."""
 94        self._kids = list(filter(lambda x: type(x) is Kid, participants))
 95        self._hosts = list(filter(lambda x: type(x) is Host, participants))
 96        self._visits = self._init_visits()
 97
 98    @staticmethod
 99    def _distance(kid, host):
100        """Calculate dinstance between a kid and a host."""
101        return math.dist(kid.get_position(), host.get_position())
102
103    @staticmethod
104    def _select_candy(candies):
105        """Return a function to select the candy with highest mass from a set of candies."""
106        return max(candies, key=lambda candy: candy.get_mass())
107    
108    @classmethod
109    def _get_closest_host_of_kid(cls, kid, hosts):
110        """Get the host that is closest to a particular kid."""
111        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))
112    
113    def _init_visits(self):
114        """Calculate all visits for the night."""
115        visits = []
116        for kid in self._kids:
117            hosts = self._hosts[:]
118            turn_index = 0
119            while hosts:
120                host = self._get_closest_host_of_kid(kid, hosts)
121                hosts.remove(host)
122                visits.append(Visit(kid, host, turn_index))
123                turn_index += 1
124                kid.set_position(host.get_position())
125        return visits
126
127    def _pop_visits(self):
128        """Remove next visits from all-visits set and return them."""
129        popped_visits = []
130        next_turn = min(self._visits, key=lambda visit: visit.turn_index).turn_index
131        for visit in self._visits:
132            if visit.turn_index == next_turn:
133                popped_visits.append(visit)
134        self._visits = [visit for visit in self._visits if visit not in popped_visits]
135        return popped_visits
136
137    def _make_a_visit(self):
138        """Make a single visit for the night and return exchanges for them."""
139        if not self._visits:
140            return None
141        visits = self._pop_visits()
142        exchanges = {}
143        for visit in visits:
144            exchanges.setdefault(visit.host, []).append(visit.kid)
145        return {host: sorted(kids, key=lambda kid: kid.get_initiative(), reverse=True) for host, kids in exchanges.items()}
146
147    def _exchange_candy(self, exchanges):
148        """Make a candy exchange."""
149        deads = set()
150        for host, kids in exchanges.items():
151            for kid in kids:
152                candy = host.remove_candy(self._select_candy)
153                if candy:
154                    kid.add_candy(candy)
155                    if kid.is_critical():
156                        deads.add(kid)
157        return deads or None
158
159    def get_victim(self):
160        """Get the first victim of the night."""
161        while True:
162            if not (exchanges:=self._make_a_visit()):
163                return None
164            if victim:=self._exchange_candy(exchanges):
165                return victim

............
----------------------------------------------------------------------
Ran 12 tests in 0.001s

OK

Дискусия
История

f1import mathf1import math
22
33
t4class Candy1:t4class Candy:
5    """Represent a Halloween in Pripyat candy."""5    """Represent a Halloween in Pripyat candy."""
66
7    def __init__(self, mass, uranium):7    def __init__(self, mass, uranium):
8        """Initializator."""8        """Initializator."""
9        self._mass = mass9        self._mass = mass
10        self._uranium = uranium10        self._uranium = uranium
11    11    
12    def get_uranium_quantity(self):12    def get_uranium_quantity(self):
13        """Get absolute content of uranium."""13        """Get absolute content of uranium."""
14        return self._mass * self._uranium14        return self._mass * self._uranium
15    15    
16    def get_mass(self):16    def get_mass(self):
17        """Get the mass of the candy."""17        """Get the mass of the candy."""
18        return self._mass18        return self._mass
1919
2020
21class Person:21class Person:
22    """Represent a Halloween in Pripyat person."""22    """Represent a Halloween in Pripyat person."""
2323
24    def __init__(self, position):24    def __init__(self, position):
25        """Initializator."""25        """Initializator."""
26        self._position = position26        self._position = position
2727
28    def get_position(self):28    def get_position(self):
29        """Get the current position of a person."""29        """Get the current position of a person."""
30        return self._position30        return self._position
3131
32    def set_position(self, position):32    def set_position(self, position):
33        """Set the current position of a person."""33        """Set the current position of a person."""
34        self._position = position34        self._position = position
3535
3636
37class Kid(Person):37class Kid(Person):
38    """Represent a Halloween in Pripyat kid."""38    """Represent a Halloween in Pripyat kid."""
3939
40    CRITICAL_URANIUM = 2040    CRITICAL_URANIUM = 20
4141
42    def __init__(self, position, initiative):42    def __init__(self, position, initiative):
43        """Initializator."""43        """Initializator."""
44        super().__init__(position)44        super().__init__(position)
45        self._candies = set()45        self._candies = set()
46        self._initiative = initiative46        self._initiative = initiative
4747
48    def get_initiative(self):48    def get_initiative(self):
49        """Get the initiative value of the kid."""49        """Get the initiative value of the kid."""
50        return self._initiative50        return self._initiative
5151
52    def add_candy(self, candy):52    def add_candy(self, candy):
53        """Add a new candy to the set of owned candies."""53        """Add a new candy to the set of owned candies."""
54        self._candies.add(candy)54        self._candies.add(candy)
55    55    
56    def is_critical(self):56    def is_critical(self):
57        """Check if the uranium mass in owned candies is critical."""57        """Check if the uranium mass in owned candies is critical."""
58        total = sum([candy.get_uranium_quantity() for candy in self._candies])58        total = sum([candy.get_uranium_quantity() for candy in self._candies])
59        return total > self.CRITICAL_URANIUM59        return total > self.CRITICAL_URANIUM
6060
6161
62class Host(Person):62class Host(Person):
63    """Represent a Halloween in Pripyat host."""63    """Represent a Halloween in Pripyat host."""
6464
65    def __init__(self, position, candies):65    def __init__(self, position, candies):
66        """Initializator."""66        """Initializator."""
67        super().__init__(position)67        super().__init__(position)
68        self._candies = {Candy(*candy) for candy in candies}68        self._candies = {Candy(*candy) for candy in candies}
6969
70    def remove_candy(self, fun):70    def remove_candy(self, fun):
71        """Remove a candy from the set of owned candies."""71        """Remove a candy from the set of owned candies."""
72        if not self._candies:72        if not self._candies:
73            return None73            return None
74        candy = fun(self._candies)74        candy = fun(self._candies)
75        self._candies.remove(candy)75        self._candies.remove(candy)
76        return candy76        return candy
7777
7878
79class Visit:79class Visit:
80    """A single visit between a kid and a host."""80    """A single visit between a kid and a host."""
8181
82    def __init__(self, kid, host, turn_index):82    def __init__(self, kid, host, turn_index):
83        """Initializator."""83        """Initializator."""
84        self.kid = kid84        self.kid = kid
85        self.host = host85        self.host = host
86        self.turn_index = turn_index86        self.turn_index = turn_index
8787
8888
89class FluxCapacitor:89class FluxCapacitor:
90    """Represent a Halloween in Pripyat simulator."""90    """Represent a Halloween in Pripyat simulator."""
9191
92    def __init__(self, participants):92    def __init__(self, participants):
93        """Initializator."""93        """Initializator."""
94        self._kids = list(filter(lambda x: type(x) is Kid, participants))94        self._kids = list(filter(lambda x: type(x) is Kid, participants))
95        self._hosts = list(filter(lambda x: type(x) is Host, participants))95        self._hosts = list(filter(lambda x: type(x) is Host, participants))
96        self._visits = self._init_visits()96        self._visits = self._init_visits()
9797
98    @staticmethod98    @staticmethod
99    def _distance(kid, host):99    def _distance(kid, host):
100        """Calculate dinstance between a kid and a host."""100        """Calculate dinstance between a kid and a host."""
101        return math.dist(kid.get_position(), host.get_position())101        return math.dist(kid.get_position(), host.get_position())
102102
103    @staticmethod103    @staticmethod
104    def _select_candy(candies):104    def _select_candy(candies):
105        """Return a function to select the candy with highest mass from a set of candies."""105        """Return a function to select the candy with highest mass from a set of candies."""
106        return max(candies, key=lambda candy: candy.get_mass())106        return max(candies, key=lambda candy: candy.get_mass())
107    107    
108    @classmethod108    @classmethod
109    def _get_closest_host_of_kid(cls, kid, hosts):109    def _get_closest_host_of_kid(cls, kid, hosts):
110        """Get the host that is closest to a particular kid."""110        """Get the host that is closest to a particular kid."""
111        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))111        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))
112    112    
113    def _init_visits(self):113    def _init_visits(self):
114        """Calculate all visits for the night."""114        """Calculate all visits for the night."""
115        visits = []115        visits = []
116        for kid in self._kids:116        for kid in self._kids:
117            hosts = self._hosts[:]117            hosts = self._hosts[:]
118            turn_index = 0118            turn_index = 0
119            while hosts:119            while hosts:
120                host = self._get_closest_host_of_kid(kid, hosts)120                host = self._get_closest_host_of_kid(kid, hosts)
121                hosts.remove(host)121                hosts.remove(host)
122                visits.append(Visit(kid, host, turn_index))122                visits.append(Visit(kid, host, turn_index))
123                turn_index += 1123                turn_index += 1
124                kid.set_position(host.get_position())124                kid.set_position(host.get_position())
125        return visits125        return visits
126126
127    def _pop_visits(self):127    def _pop_visits(self):
128        """Remove next visits from all-visits set and return them."""128        """Remove next visits from all-visits set and return them."""
129        popped_visits = []129        popped_visits = []
130        next_turn = min(self._visits, key=lambda visit: visit.turn_index).turn_index130        next_turn = min(self._visits, key=lambda visit: visit.turn_index).turn_index
131        for visit in self._visits:131        for visit in self._visits:
132            if visit.turn_index == next_turn:132            if visit.turn_index == next_turn:
133                popped_visits.append(visit)133                popped_visits.append(visit)
134        self._visits = [visit for visit in self._visits if visit not in popped_visits]134        self._visits = [visit for visit in self._visits if visit not in popped_visits]
135        return popped_visits135        return popped_visits
136136
137    def _make_a_visit(self):137    def _make_a_visit(self):
138        """Make a single visit for the night and return exchanges for them."""138        """Make a single visit for the night and return exchanges for them."""
139        if not self._visits:139        if not self._visits:
140            return None140            return None
141        visits = self._pop_visits()141        visits = self._pop_visits()
142        exchanges = {}142        exchanges = {}
143        for visit in visits:143        for visit in visits:
144            exchanges.setdefault(visit.host, []).append(visit.kid)144            exchanges.setdefault(visit.host, []).append(visit.kid)
145        return {host: sorted(kids, key=lambda kid: kid.get_initiative(), reverse=True) for host, kids in exchanges.items()}145        return {host: sorted(kids, key=lambda kid: kid.get_initiative(), reverse=True) for host, kids in exchanges.items()}
146146
147    def _exchange_candy(self, exchanges):147    def _exchange_candy(self, exchanges):
148        """Make a candy exchange."""148        """Make a candy exchange."""
149        deads = set()149        deads = set()
150        for host, kids in exchanges.items():150        for host, kids in exchanges.items():
151            for kid in kids:151            for kid in kids:
152                candy = host.remove_candy(self._select_candy)152                candy = host.remove_candy(self._select_candy)
153                if candy:153                if candy:
154                    kid.add_candy(candy)154                    kid.add_candy(candy)
155                    if kid.is_critical():155                    if kid.is_critical():
156                        deads.add(kid)156                        deads.add(kid)
157        return deads or None157        return deads or None
158158
159    def get_victim(self):159    def get_victim(self):
160        """Get the first victim of the night."""160        """Get the first victim of the night."""
161        while True:161        while True:
162            if not (exchanges:=self._make_a_visit()):162            if not (exchanges:=self._make_a_visit()):
163                return None163                return None
164            if victim:=self._exchange_candy(exchanges):164            if victim:=self._exchange_candy(exchanges):
165                return victim165                return victim
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import mathf1import math
22
33
t4class Candy:t4class Candy1:
5    """Represent a Halloween in Pripyat candy."""5    """Represent a Halloween in Pripyat candy."""
66
7    def __init__(self, mass, uranium):7    def __init__(self, mass, uranium):
8        """Initializator."""8        """Initializator."""
9        self._mass = mass9        self._mass = mass
10        self._uranium = uranium10        self._uranium = uranium
11    11    
12    def get_uranium_quantity(self):12    def get_uranium_quantity(self):
13        """Get absolute content of uranium."""13        """Get absolute content of uranium."""
14        return self._mass * self._uranium14        return self._mass * self._uranium
15    15    
16    def get_mass(self):16    def get_mass(self):
17        """Get the mass of the candy."""17        """Get the mass of the candy."""
18        return self._mass18        return self._mass
1919
2020
21class Person:21class Person:
22    """Represent a Halloween in Pripyat person."""22    """Represent a Halloween in Pripyat person."""
2323
24    def __init__(self, position):24    def __init__(self, position):
25        """Initializator."""25        """Initializator."""
26        self._position = position26        self._position = position
2727
28    def get_position(self):28    def get_position(self):
29        """Get the current position of a person."""29        """Get the current position of a person."""
30        return self._position30        return self._position
3131
32    def set_position(self, position):32    def set_position(self, position):
33        """Set the current position of a person."""33        """Set the current position of a person."""
34        self._position = position34        self._position = position
3535
3636
37class Kid(Person):37class Kid(Person):
38    """Represent a Halloween in Pripyat kid."""38    """Represent a Halloween in Pripyat kid."""
3939
40    CRITICAL_URANIUM = 2040    CRITICAL_URANIUM = 20
4141
42    def __init__(self, position, initiative):42    def __init__(self, position, initiative):
43        """Initializator."""43        """Initializator."""
44        super().__init__(position)44        super().__init__(position)
45        self._candies = set()45        self._candies = set()
46        self._initiative = initiative46        self._initiative = initiative
4747
48    def get_initiative(self):48    def get_initiative(self):
49        """Get the initiative value of the kid."""49        """Get the initiative value of the kid."""
50        return self._initiative50        return self._initiative
5151
52    def add_candy(self, candy):52    def add_candy(self, candy):
53        """Add a new candy to the set of owned candies."""53        """Add a new candy to the set of owned candies."""
54        self._candies.add(candy)54        self._candies.add(candy)
55    55    
56    def is_critical(self):56    def is_critical(self):
57        """Check if the uranium mass in owned candies is critical."""57        """Check if the uranium mass in owned candies is critical."""
58        total = sum([candy.get_uranium_quantity() for candy in self._candies])58        total = sum([candy.get_uranium_quantity() for candy in self._candies])
59        return total > self.CRITICAL_URANIUM59        return total > self.CRITICAL_URANIUM
6060
6161
62class Host(Person):62class Host(Person):
63    """Represent a Halloween in Pripyat host."""63    """Represent a Halloween in Pripyat host."""
6464
65    def __init__(self, position, candies):65    def __init__(self, position, candies):
66        """Initializator."""66        """Initializator."""
67        super().__init__(position)67        super().__init__(position)
68        self._candies = {Candy(*candy) for candy in candies}68        self._candies = {Candy(*candy) for candy in candies}
6969
70    def remove_candy(self, fun):70    def remove_candy(self, fun):
71        """Remove a candy from the set of owned candies."""71        """Remove a candy from the set of owned candies."""
72        if not self._candies:72        if not self._candies:
73            return None73            return None
74        candy = fun(self._candies)74        candy = fun(self._candies)
75        self._candies.remove(candy)75        self._candies.remove(candy)
76        return candy76        return candy
7777
7878
79class Visit:79class Visit:
80    """A single visit between a kid and a host."""80    """A single visit between a kid and a host."""
8181
82    def __init__(self, kid, host, turn_index):82    def __init__(self, kid, host, turn_index):
83        """Initializator."""83        """Initializator."""
84        self.kid = kid84        self.kid = kid
85        self.host = host85        self.host = host
86        self.turn_index = turn_index86        self.turn_index = turn_index
8787
8888
89class FluxCapacitor:89class FluxCapacitor:
90    """Represent a Halloween in Pripyat simulator."""90    """Represent a Halloween in Pripyat simulator."""
9191
92    def __init__(self, participants):92    def __init__(self, participants):
93        """Initializator."""93        """Initializator."""
94        self._kids = list(filter(lambda x: type(x) is Kid, participants))94        self._kids = list(filter(lambda x: type(x) is Kid, participants))
95        self._hosts = list(filter(lambda x: type(x) is Host, participants))95        self._hosts = list(filter(lambda x: type(x) is Host, participants))
96        self._visits = self._init_visits()96        self._visits = self._init_visits()
9797
98    @staticmethod98    @staticmethod
99    def _distance(kid, host):99    def _distance(kid, host):
100        """Calculate dinstance between a kid and a host."""100        """Calculate dinstance between a kid and a host."""
101        return math.dist(kid.get_position(), host.get_position())101        return math.dist(kid.get_position(), host.get_position())
102102
103    @staticmethod103    @staticmethod
104    def _select_candy(candies):104    def _select_candy(candies):
105        """Return a function to select the candy with highest mass from a set of candies."""105        """Return a function to select the candy with highest mass from a set of candies."""
106        return max(candies, key=lambda candy: candy.get_mass())106        return max(candies, key=lambda candy: candy.get_mass())
107    107    
108    @classmethod108    @classmethod
109    def _get_closest_host_of_kid(cls, kid, hosts):109    def _get_closest_host_of_kid(cls, kid, hosts):
110        """Get the host that is closest to a particular kid."""110        """Get the host that is closest to a particular kid."""
111        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))111        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))
112    112    
113    def _init_visits(self):113    def _init_visits(self):
114        """Calculate all visits for the night."""114        """Calculate all visits for the night."""
115        visits = []115        visits = []
116        for kid in self._kids:116        for kid in self._kids:
117            hosts = self._hosts[:]117            hosts = self._hosts[:]
118            turn_index = 0118            turn_index = 0
119            while hosts:119            while hosts:
120                host = self._get_closest_host_of_kid(kid, hosts)120                host = self._get_closest_host_of_kid(kid, hosts)
121                hosts.remove(host)121                hosts.remove(host)
122                visits.append(Visit(kid, host, turn_index))122                visits.append(Visit(kid, host, turn_index))
123                turn_index += 1123                turn_index += 1
124                kid.set_position(host.get_position())124                kid.set_position(host.get_position())
125        return visits125        return visits
126126
127    def _pop_visits(self):127    def _pop_visits(self):
128        """Remove next visits from all-visits set and return them."""128        """Remove next visits from all-visits set and return them."""
129        popped_visits = []129        popped_visits = []
130        next_turn = min(self._visits, key=lambda visit: visit.turn_index).turn_index130        next_turn = min(self._visits, key=lambda visit: visit.turn_index).turn_index
131        for visit in self._visits:131        for visit in self._visits:
132            if visit.turn_index == next_turn:132            if visit.turn_index == next_turn:
133                popped_visits.append(visit)133                popped_visits.append(visit)
134        self._visits = [visit for visit in self._visits if visit not in popped_visits]134        self._visits = [visit for visit in self._visits if visit not in popped_visits]
135        return popped_visits135        return popped_visits
136136
137    def _make_a_visit(self):137    def _make_a_visit(self):
138        """Make a single visit for the night and return exchanges for them."""138        """Make a single visit for the night and return exchanges for them."""
139        if not self._visits:139        if not self._visits:
140            return None140            return None
141        visits = self._pop_visits()141        visits = self._pop_visits()
142        exchanges = {}142        exchanges = {}
143        for visit in visits:143        for visit in visits:
144            exchanges.setdefault(visit.host, []).append(visit.kid)144            exchanges.setdefault(visit.host, []).append(visit.kid)
145        return {host: sorted(kids, key=lambda kid: kid.get_initiative(), reverse=True) for host, kids in exchanges.items()}145        return {host: sorted(kids, key=lambda kid: kid.get_initiative(), reverse=True) for host, kids in exchanges.items()}
146146
147    def _exchange_candy(self, exchanges):147    def _exchange_candy(self, exchanges):
148        """Make a candy exchange."""148        """Make a candy exchange."""
149        deads = set()149        deads = set()
150        for host, kids in exchanges.items():150        for host, kids in exchanges.items():
151            for kid in kids:151            for kid in kids:
152                candy = host.remove_candy(self._select_candy)152                candy = host.remove_candy(self._select_candy)
153                if candy:153                if candy:
154                    kid.add_candy(candy)154                    kid.add_candy(candy)
155                    if kid.is_critical():155                    if kid.is_critical():
156                        deads.add(kid)156                        deads.add(kid)
157        return deads or None157        return deads or None
158158
159    def get_victim(self):159    def get_victim(self):
160        """Get the first victim of the night."""160        """Get the first victim of the night."""
161        while True:161        while True:
162            if not (exchanges:=self._make_a_visit()):162            if not (exchanges:=self._make_a_visit()):
163                return None163                return None
164            if victim:=self._exchange_candy(exchanges):164            if victim:=self._exchange_candy(exchanges):
165                return victim165                return victim
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import mathf1import math
22
33
4class Candy:4class Candy:
nn5    """Represent a Halloween in Pripyat candy."""
56
6    def __init__(self, mass, uranium):7    def __init__(self, mass, uranium):
nn8        """Initializator."""
7        self._mass = mass9        self._mass = mass
8        self._uranium = uranium10        self._uranium = uranium
9    11    
10    def get_uranium_quantity(self):12    def get_uranium_quantity(self):
nn13        """Get absolute content of uranium."""
11        return self._mass * self._uranium14        return self._mass * self._uranium
12    15    
13    def get_mass(self):16    def get_mass(self):
nn17        """Get the mass of the candy."""
14        return self._mass18        return self._mass
1519
1620
17class Person:21class Person:
nn22    """Represent a Halloween in Pripyat person."""
1823
19    def __init__(self, position):24    def __init__(self, position):
nn25        """Initializator."""
20        self._position = position26        self._position = position
2127
22    def get_position(self):28    def get_position(self):
nn29        """Get the current position of a person."""
23        return self._position30        return self._position
2431
25    def set_position(self, position):32    def set_position(self, position):
nn33        """Set the current position of a person."""
26        self._position = position34        self._position = position
2735
2836
29class Kid(Person):37class Kid(Person):
nn38    """Represent a Halloween in Pripyat kid."""
3039
31    CRITICAL_URANIUM = 2040    CRITICAL_URANIUM = 20
3241
n33    def __init__(self, position, speed):n42    def __init__(self, position, initiative):
43        """Initializator."""
34        super().__init__(position)44        super().__init__(position)
35        self._candies = set()45        self._candies = set()
n36        self._speed = speedn46        self._initiative = initiative
3747
n38    def get_speed(self):n48    def get_initiative(self):
49        """Get the initiative value of the kid."""
39        return self._speed50        return self._initiative
4051
41    def add_candy(self, candy):52    def add_candy(self, candy):
nn53        """Add a new candy to the set of owned candies."""
42        self._candies.add(candy)54        self._candies.add(candy)
43    55    
44    def is_critical(self):56    def is_critical(self):
nn57        """Check if the uranium mass in owned candies is critical."""
45        total = sum([candy.get_uranium_quantity() for candy in self._candies])58        total = sum([candy.get_uranium_quantity() for candy in self._candies])
46        return total > self.CRITICAL_URANIUM59        return total > self.CRITICAL_URANIUM
4760
4861
49class Host(Person):62class Host(Person):
nn63    """Represent a Halloween in Pripyat host."""
5064
51    def __init__(self, position, candies):65    def __init__(self, position, candies):
nn66        """Initializator."""
52        super().__init__(position)67        super().__init__(position)
53        self._candies = {Candy(*candy) for candy in candies}68        self._candies = {Candy(*candy) for candy in candies}
5469
55    def remove_candy(self, fun):70    def remove_candy(self, fun):
nn71        """Remove a candy from the set of owned candies."""
56        if not self._candies:72        if not self._candies:
57            return None73            return None
58        candy = fun(self._candies)74        candy = fun(self._candies)
59        self._candies.remove(candy)75        self._candies.remove(candy)
60        return candy76        return candy
6177
6278
63class Visit:79class Visit:
nn80    """A single visit between a kid and a host."""
6481
n65    def __init__(self, kid, host, absolute_time):n82    def __init__(self, kid, host, turn_index):
83        """Initializator."""
66        self.kid = kid84        self.kid = kid
67        self.host = host85        self.host = host
n68        self.absolute_time = absolute_timen86        self.turn_index = turn_index
6987
7088
71class FluxCapacitor:89class FluxCapacitor:
nn90    """Represent a Halloween in Pripyat simulator."""
7291
73    def __init__(self, participants):92    def __init__(self, participants):
nn93        """Initializator."""
74        self._kids = list(filter(lambda x: type(x) is Kid, participants))94        self._kids = list(filter(lambda x: type(x) is Kid, participants))
75        self._hosts = list(filter(lambda x: type(x) is Host, participants))95        self._hosts = list(filter(lambda x: type(x) is Host, participants))
76        self._visits = self._init_visits()96        self._visits = self._init_visits()
7797
78    @staticmethod98    @staticmethod
79    def _distance(kid, host):99    def _distance(kid, host):
nn100        """Calculate dinstance between a kid and a host."""
80        return math.dist(kid.get_position(), host.get_position())101        return math.dist(kid.get_position(), host.get_position())
81102
82    @staticmethod103    @staticmethod
83    def _select_candy(candies):104    def _select_candy(candies):
nn105        """Return a function to select the candy with highest mass from a set of candies."""
84        return max(candies, key=lambda candy: candy.get_mass())106        return max(candies, key=lambda candy: candy.get_mass())
85    107    
86    @classmethod108    @classmethod
87    def _get_closest_host_of_kid(cls, kid, hosts):109    def _get_closest_host_of_kid(cls, kid, hosts):
nn110        """Get the host that is closest to a particular kid."""
88        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))111        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))
89    112    
90    def _init_visits(self):113    def _init_visits(self):
nn114        """Calculate all visits for the night."""
91        visits = []115        visits = []
92        for kid in self._kids:116        for kid in self._kids:
n93            absolute_time = 0n
94            hosts = self._hosts[:]117            hosts = self._hosts[:]
nn118            turn_index = 0
95            while hosts:119            while hosts:
96                host = self._get_closest_host_of_kid(kid, hosts)120                host = self._get_closest_host_of_kid(kid, hosts)
97                hosts.remove(host)121                hosts.remove(host)
n98                distance = math.dist(kid.get_position(), host.get_position())n
99                time_delta = distance / kid.get_speed()
100                absolute_time = absolute_time + time_delta
101                visits.append(Visit(kid, host, absolute_time))122                visits.append(Visit(kid, host, turn_index))
123                turn_index += 1
124                kid.set_position(host.get_position())
102        return visits125        return visits
n103    n126 
104    def _pop_visits(self):127    def _pop_visits(self):
nn128        """Remove next visits from all-visits set and return them."""
105        popped_visits = []129        popped_visits = []
n106        next_time = min(self._visits, key=lambda visit: visit.absolute_time).absolute_timen130        next_turn = min(self._visits, key=lambda visit: visit.turn_index).turn_index
107        for visit in self._visits:131        for visit in self._visits:
n108            if visit.absolute_time == next_time:n132            if visit.turn_index == next_turn:
109                popped_visits.append(visit)133                popped_visits.append(visit)
110        self._visits = [visit for visit in self._visits if visit not in popped_visits]134        self._visits = [visit for visit in self._visits if visit not in popped_visits]
111        return popped_visits135        return popped_visits
112136
113    def _make_a_visit(self):137    def _make_a_visit(self):
nn138        """Make a single visit for the night and return exchanges for them."""
114        if not self._visits:139        if not self._visits:
115            return None140            return None
116        visits = self._pop_visits()141        visits = self._pop_visits()
117        exchanges = {}142        exchanges = {}
118        for visit in visits:143        for visit in visits:
119            exchanges.setdefault(visit.host, []).append(visit.kid)144            exchanges.setdefault(visit.host, []).append(visit.kid)
n120        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in exchanges.items()}n145        return {host: sorted(kids, key=lambda kid: kid.get_initiative(), reverse=True) for host, kids in exchanges.items()}
121146
122    def _exchange_candy(self, exchanges):147    def _exchange_candy(self, exchanges):
nn148        """Make a candy exchange."""
149        deads = set()
123        for host, kids in exchanges.items():150        for host, kids in exchanges.items():
124            for kid in kids:151            for kid in kids:
125                candy = host.remove_candy(self._select_candy)152                candy = host.remove_candy(self._select_candy)
126                if candy:153                if candy:
127                    kid.add_candy(candy)154                    kid.add_candy(candy)
128                    if kid.is_critical():155                    if kid.is_critical():
n129                        return kidn156                        deads.add(kid)
130        return None157        return deads or None
131    158 
132    def get_victim(self):159    def get_victim(self):
tt160        """Get the first victim of the night."""
133        while True:161        while True:
134            if not (exchanges:=self._make_a_visit()):162            if not (exchanges:=self._make_a_visit()):
135                return None163                return None
136            if victim:=self._exchange_candy(exchanges):164            if victim:=self._exchange_candy(exchanges):
137                return victim165                return victim
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import mathf1import math
22
33
4class Candy:4class Candy:
55
6    def __init__(self, mass, uranium):6    def __init__(self, mass, uranium):
7        self._mass = mass7        self._mass = mass
8        self._uranium = uranium8        self._uranium = uranium
9    9    
10    def get_uranium_quantity(self):10    def get_uranium_quantity(self):
11        return self._mass * self._uranium11        return self._mass * self._uranium
12    12    
13    def get_mass(self):13    def get_mass(self):
14        return self._mass14        return self._mass
1515
1616
17class Person:17class Person:
1818
19    def __init__(self, position):19    def __init__(self, position):
20        self._position = position20        self._position = position
2121
22    def get_position(self):22    def get_position(self):
23        return self._position23        return self._position
2424
25    def set_position(self, position):25    def set_position(self, position):
26        self._position = position26        self._position = position
2727
2828
29class Kid(Person):29class Kid(Person):
3030
31    CRITICAL_URANIUM = 2031    CRITICAL_URANIUM = 20
3232
33    def __init__(self, position, speed):33    def __init__(self, position, speed):
34        super().__init__(position)34        super().__init__(position)
35        self._candies = set()35        self._candies = set()
36        self._speed = speed36        self._speed = speed
3737
38    def get_speed(self):38    def get_speed(self):
39        return self._speed39        return self._speed
4040
41    def add_candy(self, candy):41    def add_candy(self, candy):
42        self._candies.add(candy)42        self._candies.add(candy)
43    43    
44    def is_critical(self):44    def is_critical(self):
45        total = sum([candy.get_uranium_quantity() for candy in self._candies])45        total = sum([candy.get_uranium_quantity() for candy in self._candies])
46        return total > self.CRITICAL_URANIUM46        return total > self.CRITICAL_URANIUM
4747
4848
49class Host(Person):49class Host(Person):
5050
51    def __init__(self, position, candies):51    def __init__(self, position, candies):
52        super().__init__(position)52        super().__init__(position)
53        self._candies = {Candy(*candy) for candy in candies}53        self._candies = {Candy(*candy) for candy in candies}
5454
55    def remove_candy(self, fun):55    def remove_candy(self, fun):
56        if not self._candies:56        if not self._candies:
57            return None57            return None
58        candy = fun(self._candies)58        candy = fun(self._candies)
59        self._candies.remove(candy)59        self._candies.remove(candy)
60        return candy60        return candy
6161
6262
63class Visit:63class Visit:
6464
65    def __init__(self, kid, host, absolute_time):65    def __init__(self, kid, host, absolute_time):
66        self.kid = kid66        self.kid = kid
67        self.host = host67        self.host = host
68        self.absolute_time = absolute_time68        self.absolute_time = absolute_time
6969
7070
71class FluxCapacitor:71class FluxCapacitor:
7272
73    def __init__(self, participants):73    def __init__(self, participants):
74        self._kids = list(filter(lambda x: type(x) is Kid, participants))74        self._kids = list(filter(lambda x: type(x) is Kid, participants))
75        self._hosts = list(filter(lambda x: type(x) is Host, participants))75        self._hosts = list(filter(lambda x: type(x) is Host, participants))
76        self._visits = self._init_visits()76        self._visits = self._init_visits()
7777
78    @staticmethod78    @staticmethod
79    def _distance(kid, host):79    def _distance(kid, host):
80        return math.dist(kid.get_position(), host.get_position())80        return math.dist(kid.get_position(), host.get_position())
8181
82    @staticmethod82    @staticmethod
83    def _select_candy(candies):83    def _select_candy(candies):
84        return max(candies, key=lambda candy: candy.get_mass())84        return max(candies, key=lambda candy: candy.get_mass())
85    85    
86    @classmethod86    @classmethod
87    def _get_closest_host_of_kid(cls, kid, hosts):87    def _get_closest_host_of_kid(cls, kid, hosts):
88        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))88        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))
89    89    
90    def _init_visits(self):90    def _init_visits(self):
91        visits = []91        visits = []
92        for kid in self._kids:92        for kid in self._kids:
93            absolute_time = 093            absolute_time = 0
94            hosts = self._hosts[:]94            hosts = self._hosts[:]
95            while hosts:95            while hosts:
96                host = self._get_closest_host_of_kid(kid, hosts)96                host = self._get_closest_host_of_kid(kid, hosts)
97                hosts.remove(host)97                hosts.remove(host)
98                distance = math.dist(kid.get_position(), host.get_position())98                distance = math.dist(kid.get_position(), host.get_position())
99                time_delta = distance / kid.get_speed()99                time_delta = distance / kid.get_speed()
100                absolute_time = absolute_time + time_delta100                absolute_time = absolute_time + time_delta
101                visits.append(Visit(kid, host, absolute_time))101                visits.append(Visit(kid, host, absolute_time))
102        return visits102        return visits
103    103    
104    def _pop_visits(self):104    def _pop_visits(self):
105        popped_visits = []105        popped_visits = []
106        next_time = min(self._visits, key=lambda visit: visit.absolute_time).absolute_time106        next_time = min(self._visits, key=lambda visit: visit.absolute_time).absolute_time
107        for visit in self._visits:107        for visit in self._visits:
108            if visit.absolute_time == next_time:108            if visit.absolute_time == next_time:
109                popped_visits.append(visit)109                popped_visits.append(visit)
110        self._visits = [visit for visit in self._visits if visit not in popped_visits]110        self._visits = [visit for visit in self._visits if visit not in popped_visits]
111        return popped_visits111        return popped_visits
112112
113    def _make_a_visit(self):113    def _make_a_visit(self):
114        if not self._visits:114        if not self._visits:
115            return None115            return None
116        visits = self._pop_visits()116        visits = self._pop_visits()
117        exchanges = {}117        exchanges = {}
118        for visit in visits:118        for visit in visits:
119            exchanges.setdefault(visit.host, []).append(visit.kid)119            exchanges.setdefault(visit.host, []).append(visit.kid)
120        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in exchanges.items()}120        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in exchanges.items()}
121121
122    def _exchange_candy(self, exchanges):122    def _exchange_candy(self, exchanges):
123        for host, kids in exchanges.items():123        for host, kids in exchanges.items():
124            for kid in kids:124            for kid in kids:
125                candy = host.remove_candy(self._select_candy)125                candy = host.remove_candy(self._select_candy)
126                if candy:126                if candy:
127                    kid.add_candy(candy)127                    kid.add_candy(candy)
128                    if kid.is_critical():128                    if kid.is_critical():
129                        return kid129                        return kid
130        return None130        return None
131    131    
132    def get_victim(self):132    def get_victim(self):
133        while True:133        while True:
134            if not (exchanges:=self._make_a_visit()):134            if not (exchanges:=self._make_a_visit()):
135                return None135                return None
136            if victim:=self._exchange_candy(exchanges):136            if victim:=self._exchange_candy(exchanges):
137                return victim137                return victim
t138 t
139Visit.__repr__ = lambda x: f"{x.kid} visits {x.host} at {x.absolute_time}"
140Person.__repr__ = lambda x: x.name
141kid1 = Kid((1, 1), 1)
142kid1.name = 'kid1'
143kid2 = Kid((7, 1), 2)
144kid2.name = 'kid2'
145host1 = Host((2, 1), [(15, 1), (14, 0)])
146host1.name = 'host1'
147host2 = Host((6, 1), [(15, 1), (16, 1)])
148host2.name = 'host2'
149print(FluxCapacitor({kid1, kid2, host1, host2}).get_victim())
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import mathf1import math
22
33
4class Candy:4class Candy:
55
6    def __init__(self, mass, uranium):6    def __init__(self, mass, uranium):
7        self._mass = mass7        self._mass = mass
8        self._uranium = uranium8        self._uranium = uranium
9    9    
10    def get_uranium_quantity(self):10    def get_uranium_quantity(self):
11        return self._mass * self._uranium11        return self._mass * self._uranium
12    12    
13    def get_mass(self):13    def get_mass(self):
14        return self._mass14        return self._mass
1515
1616
17class Person:17class Person:
1818
19    def __init__(self, position):19    def __init__(self, position):
20        self._position = position20        self._position = position
2121
22    def get_position(self):22    def get_position(self):
23        return self._position23        return self._position
2424
25    def set_position(self, position):25    def set_position(self, position):
26        self._position = position26        self._position = position
2727
2828
29class Kid(Person):29class Kid(Person):
3030
31    CRITICAL_URANIUM = 2031    CRITICAL_URANIUM = 20
3232
33    def __init__(self, position, speed):33    def __init__(self, position, speed):
34        super().__init__(position)34        super().__init__(position)
n35        self._candies = {}n35        self._candies = set()
36        self._speed = speed36        self._speed = speed
3737
38    def get_speed(self):38    def get_speed(self):
39        return self._speed39        return self._speed
4040
41    def add_candy(self, candy):41    def add_candy(self, candy):
42        self._candies.add(candy)42        self._candies.add(candy)
43    43    
44    def is_critical(self):44    def is_critical(self):
45        total = sum([candy.get_uranium_quantity() for candy in self._candies])45        total = sum([candy.get_uranium_quantity() for candy in self._candies])
46        return total > self.CRITICAL_URANIUM46        return total > self.CRITICAL_URANIUM
4747
4848
49class Host(Person):49class Host(Person):
5050
51    def __init__(self, position, candies):51    def __init__(self, position, candies):
52        super().__init__(position)52        super().__init__(position)
n53        self._candies = [Candy(*candy) for candy in candies]n53        self._candies = {Candy(*candy) for candy in candies}
5454
55    def remove_candy(self, fun):55    def remove_candy(self, fun):
56        if not self._candies:56        if not self._candies:
57            return None57            return None
nn58        candy = fun(self._candies)
58        return self._candies.remove(fun(self._candies))59        self._candies.remove(candy)
60        return candy
5961
6062
61class Visit:63class Visit:
6264
63    def __init__(self, kid, host, absolute_time):65    def __init__(self, kid, host, absolute_time):
64        self.kid = kid66        self.kid = kid
65        self.host = host67        self.host = host
66        self.absolute_time = absolute_time68        self.absolute_time = absolute_time
6769
6870
69class FluxCapacitor:71class FluxCapacitor:
7072
71    def __init__(self, participants):73    def __init__(self, participants):
n72        self._kids = filter(lambda x: type(x) is Kid, participants)n74        self._kids = list(filter(lambda x: type(x) is Kid, participants))
73        self._hosts = filter(lambda x: type(x) is Host, participants)75        self._hosts = list(filter(lambda x: type(x) is Host, participants))
74        self._visits = self._get_visits()76        self._visits = self._init_visits()
7577
76    @staticmethod78    @staticmethod
77    def _distance(kid, host):79    def _distance(kid, host):
78        return math.dist(kid.get_position(), host.get_position())80        return math.dist(kid.get_position(), host.get_position())
7981
80    @staticmethod82    @staticmethod
81    def _select_candy(candies):83    def _select_candy(candies):
82        return max(candies, key=lambda candy: candy.get_mass())84        return max(candies, key=lambda candy: candy.get_mass())
83    85    
84    @classmethod86    @classmethod
85    def _get_closest_host_of_kid(cls, kid, hosts):87    def _get_closest_host_of_kid(cls, kid, hosts):
86        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))88        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))
87    89    
n88    def _get_visits(self):n90    def _init_visits(self):
89        visits = []91        visits = []
90        for kid in self._kids:92        for kid in self._kids:
91            absolute_time = 093            absolute_time = 0
92            hosts = self._hosts[:]94            hosts = self._hosts[:]
93            while hosts:95            while hosts:
94                host = self._get_closest_host_of_kid(kid, hosts)96                host = self._get_closest_host_of_kid(kid, hosts)
95                hosts.remove(host)97                hosts.remove(host)
96                distance = math.dist(kid.get_position(), host.get_position())98                distance = math.dist(kid.get_position(), host.get_position())
97                time_delta = distance / kid.get_speed()99                time_delta = distance / kid.get_speed()
98                absolute_time = absolute_time + time_delta100                absolute_time = absolute_time + time_delta
n99                visits.append(kid, host, absolute_time)n101                visits.append(Visit(kid, host, absolute_time))
100        return visits102        return visits
101    103    
102    def _pop_visits(self):104    def _pop_visits(self):
n103        poped_visits = {}n105        popped_visits = []
104        next_time = min(self._visits, key=lambda visit: visit.absolute_time)106        next_time = min(self._visits, key=lambda visit: visit.absolute_time).absolute_time
105        for visit in self._visits:107        for visit in self._visits:
106            if visit.absolute_time == next_time:108            if visit.absolute_time == next_time:
n107                poped_visits.update(self._visits.pop(visit))n109                popped_visits.append(visit)
110        self._visits = [visit for visit in self._visits if visit not in popped_visits]
108        return poped_visits111        return popped_visits
109112
110    def _make_a_visit(self):113    def _make_a_visit(self):
nn114        if not self._visits:
115            return None
111        visits = self._pop_visits()116        visits = self._pop_visits()
n112        if not visits:n
113            return None
114        exchanges = {}117        exchanges = {}
115        for visit in visits:118        for visit in visits:
116            exchanges.setdefault(visit.host, []).append(visit.kid)119            exchanges.setdefault(visit.host, []).append(visit.kid)
n117        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in exchanges}n120        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in exchanges.items()}
118121
119    def _exchange_candy(self, exchanges):122    def _exchange_candy(self, exchanges):
120        for host, kids in exchanges.items():123        for host, kids in exchanges.items():
121            for kid in kids:124            for kid in kids:
122                candy = host.remove_candy(self._select_candy)125                candy = host.remove_candy(self._select_candy)
123                if candy:126                if candy:
124                    kid.add_candy(candy)127                    kid.add_candy(candy)
125                    if kid.is_critical():128                    if kid.is_critical():
126                        return kid129                        return kid
127        return None130        return None
128    131    
129    def get_victim(self):132    def get_victim(self):
130        while True:133        while True:
131            if not (exchanges:=self._make_a_visit()):134            if not (exchanges:=self._make_a_visit()):
132                return None135                return None
133            if victim:=self._exchange_candy(exchanges):136            if victim:=self._exchange_candy(exchanges):
134                return victim137                return victim
tt138 
139Visit.__repr__ = lambda x: f"{x.kid} visits {x.host} at {x.absolute_time}"
140Person.__repr__ = lambda x: x.name
141kid1 = Kid((1, 1), 1)
142kid1.name = 'kid1'
143kid2 = Kid((7, 1), 2)
144kid2.name = 'kid2'
145host1 = Host((2, 1), [(15, 1), (14, 0)])
146host1.name = 'host1'
147host2 = Host((6, 1), [(15, 1), (16, 1)])
148host2.name = 'host2'
149print(FluxCapacitor({kid1, kid2, host1, host2}).get_victim())
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import mathf1import math
22
33
4class Candy:4class Candy:
55
6    def __init__(self, mass, uranium):6    def __init__(self, mass, uranium):
7        self._mass = mass7        self._mass = mass
8        self._uranium = uranium8        self._uranium = uranium
9    9    
10    def get_uranium_quantity(self):10    def get_uranium_quantity(self):
11        return self._mass * self._uranium11        return self._mass * self._uranium
12    12    
13    def get_mass(self):13    def get_mass(self):
14        return self._mass14        return self._mass
1515
1616
17class Person:17class Person:
1818
19    def __init__(self, position):19    def __init__(self, position):
20        self._position = position20        self._position = position
2121
22    def get_position(self):22    def get_position(self):
23        return self._position23        return self._position
2424
25    def set_position(self, position):25    def set_position(self, position):
26        self._position = position26        self._position = position
2727
2828
29class Kid(Person):29class Kid(Person):
3030
31    CRITICAL_URANIUM = 2031    CRITICAL_URANIUM = 20
3232
33    def __init__(self, position, speed):33    def __init__(self, position, speed):
34        super().__init__(position)34        super().__init__(position)
35        self._candies = {}35        self._candies = {}
36        self._speed = speed36        self._speed = speed
3737
38    def get_speed(self):38    def get_speed(self):
39        return self._speed39        return self._speed
4040
41    def add_candy(self, candy):41    def add_candy(self, candy):
42        self._candies.add(candy)42        self._candies.add(candy)
43    43    
44    def is_critical(self):44    def is_critical(self):
45        total = sum([candy.get_uranium_quantity() for candy in self._candies])45        total = sum([candy.get_uranium_quantity() for candy in self._candies])
46        return total > self.CRITICAL_URANIUM46        return total > self.CRITICAL_URANIUM
4747
4848
49class Host(Person):49class Host(Person):
5050
51    def __init__(self, position, candies):51    def __init__(self, position, candies):
52        super().__init__(position)52        super().__init__(position)
53        self._candies = [Candy(*candy) for candy in candies]53        self._candies = [Candy(*candy) for candy in candies]
5454
55    def remove_candy(self, fun):55    def remove_candy(self, fun):
56        if not self._candies:56        if not self._candies:
57            return None57            return None
58        return self._candies.remove(fun(self._candies))58        return self._candies.remove(fun(self._candies))
5959
6060
nn61class Visit:
62 
63    def __init__(self, kid, host, absolute_time):
64        self.kid = kid
65        self.host = host
66        self.absolute_time = absolute_time
67 
68 
61class FluxCapacitor:69class FluxCapacitor:
6270
63    def __init__(self, participants):71    def __init__(self, participants):
64        self._kids = filter(lambda x: type(x) is Kid, participants)72        self._kids = filter(lambda x: type(x) is Kid, participants)
65        self._hosts = filter(lambda x: type(x) is Host, participants)73        self._hosts = filter(lambda x: type(x) is Host, participants)
n66        self._visits = {kid: [] for kid in self._kids}n74        self._visits = self._get_visits()
67        self._next_time_of_arrival = {kid: self._get_next_host_and_time_of_kid(kid) for kid in self._kids}75 
76    @staticmethod
77    def _distance(kid, host):
78        return math.dist(kid.get_position(), host.get_position())
6879
69    @staticmethod80    @staticmethod
70    def _select_candy(candies):81    def _select_candy(candies):
71        return max(candies, key=lambda candy: candy.get_mass())82        return max(candies, key=lambda candy: candy.get_mass())
nn83    
84    @classmethod
85    def _get_closest_host_of_kid(cls, kid, hosts):
86        return min(hosts, key=lambda host: (cls._distance(kid, host), host.get_position()))
87    
88    def _get_visits(self):
89        visits = []
90        for kid in self._kids:
91            absolute_time = 0
92            hosts = self._hosts[:]
93            while hosts:
94                host = self._get_closest_host_of_kid(kid, hosts)
95                hosts.remove(host)
96                distance = math.dist(kid.get_position(), host.get_position())
97                time_delta = distance / kid.get_speed()
98                absolute_time = absolute_time + time_delta
99                visits.append(kid, host, absolute_time)
100        return visits
101    
102    def _pop_visits(self):
103        poped_visits = {}
104        next_time = min(self._visits, key=lambda visit: visit.absolute_time)
105        for visit in self._visits:
106            if visit.absolute_time == next_time:
107                poped_visits.update(self._visits.pop(visit))
108        return poped_visits
72109
n73    def _get_next_host_and_time_of_kid(self, kid):n110    def _make_a_visit(self):
74        if not(host:=self._get_closest_host_to_kid(kid)):111        visits = self._pop_visits()
75            return None, None112        if not visits:
76        distance = math.dist(kid.get_position(), host.get_position())
77        return host, distance / kid.get_speed()
78 
79    def _get_closest_host_of_kid(self, kid):
80        distances = {}
81        for host in self._hosts:
82            if host in self._visits[kid]:
83                continue
84            distances[host] = math.dist(kid.get_position(), host.get_position())
85        if distances:
86            min_distance = min(distances)
87            hosts = [host for host, distance in distances.items() if distance == min_distance]
88            return min(hosts, key=lambda host: host.get_position())
89        return None113            return None
90 114        exchanges = {}
91    def _move_a_turn(self):115        for visit in visits:
92        next_arrival_time = min([arrival_time for _, arrival_time in self._next_time_of_arrival.values() if arrival_time is not None])
93        next_arrivals = {kid: arrival for kid, arrival in self._next_time_of_arrival.items() if arrival[1] == next_arrival_time}
94        all_arrivals = {}
95        for kid, (host, _) in next_arrivals.items():
96            all_arrivals.setdefault(host, []).append(kid)116            exchanges.setdefault(visit.host, []).append(visit.kid)
97        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in all_arrivals}117        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in exchanges}
98118
99    def _exchange_candy(self, exchanges):119    def _exchange_candy(self, exchanges):
100        for host, kids in exchanges.items():120        for host, kids in exchanges.items():
101            for kid in kids:121            for kid in kids:
102                candy = host.remove_candy(self._select_candy)122                candy = host.remove_candy(self._select_candy)
103                if candy:123                if candy:
104                    kid.add_candy(candy)124                    kid.add_candy(candy)
105                    if kid.is_critical():125                    if kid.is_critical():
106                        return kid126                        return kid
n107                next_host, time_delta = self._get_next_host_and_time_of_kid(kid)n
108                if next_host is None:
109                    self._next_time_of_arrival[kid] = None, None
110                else:
111                    self._next_time_of_arrival[kid] = next_host, time_delta + self._next_time_of_arrival[kid][1]
112        return None127        return None
113    128    
114    def get_victim(self):129    def get_victim(self):
115        while True:130        while True:
t116            if not (exchanges:=self._move_a_turn()):t131            if not (exchanges:=self._make_a_visit()):
117                return None132                return None
118            if victim:=self._exchange_candy(exchanges):133            if victim:=self._exchange_candy(exchanges):
119                return victim134                return victim
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1import mathf1import math
22
33
4class Candy:4class Candy:
55
6    def __init__(self, mass, uranium):6    def __init__(self, mass, uranium):
7        self._mass = mass7        self._mass = mass
8        self._uranium = uranium8        self._uranium = uranium
9    9    
10    def get_uranium_quantity(self):10    def get_uranium_quantity(self):
11        return self._mass * self._uranium11        return self._mass * self._uranium
12    12    
13    def get_mass(self):13    def get_mass(self):
14        return self._mass14        return self._mass
1515
1616
17class Person:17class Person:
1818
19    def __init__(self, position):19    def __init__(self, position):
20        self._position = position20        self._position = position
2121
22    def get_position(self):22    def get_position(self):
23        return self._position23        return self._position
2424
25    def set_position(self, position):25    def set_position(self, position):
26        self._position = position26        self._position = position
2727
2828
29class Kid(Person):29class Kid(Person):
3030
31    CRITICAL_URANIUM = 2031    CRITICAL_URANIUM = 20
3232
33    def __init__(self, position, speed):33    def __init__(self, position, speed):
34        super().__init__(position)34        super().__init__(position)
35        self._candies = {}35        self._candies = {}
36        self._speed = speed36        self._speed = speed
3737
38    def get_speed(self):38    def get_speed(self):
39        return self._speed39        return self._speed
4040
41    def add_candy(self, candy):41    def add_candy(self, candy):
42        self._candies.add(candy)42        self._candies.add(candy)
43    43    
44    def is_critical(self):44    def is_critical(self):
45        total = sum([candy.get_uranium_quantity() for candy in self._candies])45        total = sum([candy.get_uranium_quantity() for candy in self._candies])
46        return total > self.CRITICAL_URANIUM46        return total > self.CRITICAL_URANIUM
4747
4848
49class Host(Person):49class Host(Person):
5050
51    def __init__(self, position, candies):51    def __init__(self, position, candies):
52        super().__init__(position)52        super().__init__(position)
53        self._candies = [Candy(*candy) for candy in candies]53        self._candies = [Candy(*candy) for candy in candies]
5454
55    def remove_candy(self, fun):55    def remove_candy(self, fun):
56        if not self._candies:56        if not self._candies:
57            return None57            return None
58        return self._candies.remove(fun(self._candies))58        return self._candies.remove(fun(self._candies))
5959
6060
61class FluxCapacitor:61class FluxCapacitor:
6262
63    def __init__(self, participants):63    def __init__(self, participants):
64        self._kids = filter(lambda x: type(x) is Kid, participants)64        self._kids = filter(lambda x: type(x) is Kid, participants)
65        self._hosts = filter(lambda x: type(x) is Host, participants)65        self._hosts = filter(lambda x: type(x) is Host, participants)
66        self._visits = {kid: [] for kid in self._kids}66        self._visits = {kid: [] for kid in self._kids}
67        self._next_time_of_arrival = {kid: self._get_next_host_and_time_of_kid(kid) for kid in self._kids}67        self._next_time_of_arrival = {kid: self._get_next_host_and_time_of_kid(kid) for kid in self._kids}
6868
69    @staticmethod69    @staticmethod
70    def _select_candy(candies):70    def _select_candy(candies):
71        return max(candies, key=lambda candy: candy.get_mass())71        return max(candies, key=lambda candy: candy.get_mass())
7272
73    def _get_next_host_and_time_of_kid(self, kid):73    def _get_next_host_and_time_of_kid(self, kid):
74        if not(host:=self._get_closest_host_to_kid(kid)):74        if not(host:=self._get_closest_host_to_kid(kid)):
75            return None, None75            return None, None
76        distance = math.dist(kid.get_position(), host.get_position())76        distance = math.dist(kid.get_position(), host.get_position())
77        return host, distance / kid.get_speed()77        return host, distance / kid.get_speed()
7878
79    def _get_closest_host_of_kid(self, kid):79    def _get_closest_host_of_kid(self, kid):
80        distances = {}80        distances = {}
81        for host in self._hosts:81        for host in self._hosts:
82            if host in self._visits[kid]:82            if host in self._visits[kid]:
83                continue83                continue
84            distances[host] = math.dist(kid.get_position(), host.get_position())84            distances[host] = math.dist(kid.get_position(), host.get_position())
85        if distances:85        if distances:
86            min_distance = min(distances)86            min_distance = min(distances)
87            hosts = [host for host, distance in distances.items() if distance == min_distance]87            hosts = [host for host, distance in distances.items() if distance == min_distance]
88            return min(hosts, key=lambda host: host.get_position())88            return min(hosts, key=lambda host: host.get_position())
89        return None89        return None
9090
91    def _move_a_turn(self):91    def _move_a_turn(self):
92        next_arrival_time = min([arrival_time for _, arrival_time in self._next_time_of_arrival.values() if arrival_time is not None])92        next_arrival_time = min([arrival_time for _, arrival_time in self._next_time_of_arrival.values() if arrival_time is not None])
93        next_arrivals = {kid: arrival for kid, arrival in self._next_time_of_arrival.items() if arrival[1] == next_arrival_time}93        next_arrivals = {kid: arrival for kid, arrival in self._next_time_of_arrival.items() if arrival[1] == next_arrival_time}
94        all_arrivals = {}94        all_arrivals = {}
95        for kid, (host, _) in next_arrivals.items():95        for kid, (host, _) in next_arrivals.items():
96            all_arrivals.setdefault(host, []).append(kid)96            all_arrivals.setdefault(host, []).append(kid)
97        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in all_arrivals}97        return {host: sorted(kids, key=lambda kid: kid.get_speed()) for host, kids in all_arrivals}
9898
99    def _exchange_candy(self, exchanges):99    def _exchange_candy(self, exchanges):
100        for host, kids in exchanges.items():100        for host, kids in exchanges.items():
101            for kid in kids:101            for kid in kids:
102                candy = host.remove_candy(self._select_candy)102                candy = host.remove_candy(self._select_candy)
103                if candy:103                if candy:
104                    kid.add_candy(candy)104                    kid.add_candy(candy)
105                    if kid.is_critical():105                    if kid.is_critical():
106                        return kid106                        return kid
tt107                next_host, time_delta = self._get_next_host_and_time_of_kid(kid)
108                if next_host is None:
109                    self._next_time_of_arrival[kid] = None, None
110                else:
111                    self._next_time_of_arrival[kid] = next_host, time_delta + self._next_time_of_arrival[kid][1]
107        return None112        return None
108    113    
109    def get_victim(self):114    def get_victim(self):
110        while True:115        while True:
111            if not (exchanges:=self._move_a_turn()):116            if not (exchanges:=self._move_a_turn()):
112                return None117                return None
113            if victim:=self._exchange_candy(exchanges):118            if victim:=self._exchange_candy(exchanges):
114                return victim119                return victim
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op