···11+import math
22+import fileinput
33+from copy import deepcopy
44+from collections import Counter
55+66+77+def dist(particle):
88+ x, y, z = particle[0]
99+ return math.sqrt(x**2 + y**2 + z**2)
1010+1111+1212+def simulate(particles, iterations, collisions=False):
1313+ particles = deepcopy(particles)
1414+ closest_particle = Counter()
1515+ destroyed = set()
1616+1717+ for _ in range(iterations):
1818+ # Update particle velocities and positions
1919+ for i, (p, v, a) in enumerate(particles):
2020+ # Don't update destroyed particles
2121+ if i in destroyed:
2222+ continue
2323+2424+ v[0] += a[0]
2525+ v[1] += a[1]
2626+ v[2] += a[2]
2727+2828+ p[0] += v[0]
2929+ p[1] += v[1]
3030+ p[2] += v[2]
3131+3232+ if collisions:
3333+ for i in range(len(particles)):
3434+ if i in destroyed:
3535+ continue
3636+3737+ for j in range(i + 1, len(particles)):
3838+ if j in destroyed:
3939+ continue
4040+4141+ if particles[i][0] == particles[j][0]:
4242+ destroyed.add(i)
4343+ destroyed.add(j)
4444+4545+ candidates = [(i, p) for i, p in enumerate(particles) if i not in destroyed]
4646+ idx, _ = min(candidates, key=lambda x: dist(x[1]))
4747+ closest_particle[idx] += 1
4848+4949+ closest, _ = closest_particle.most_common()[0]
5050+ remaining = len(particles) - len(destroyed)
5151+5252+ return closest, remaining
5353+5454+5555+# Read puzzle input
5656+PARTICLES = []
5757+5858+for line in fileinput.input():
5959+ parts = (x[3:-1] for x in line.strip().split(', '))
6060+ p, v, a = ([int(n) for n in part.split(',')] for part in parts)
6161+ PARTICLES.append([p, v, a])
6262+6363+# Naively guess stopping criteria (TODO: solve analytically)
6464+print "Long-term closest particle to origin:", simulate(PARTICLES, 500)[0]
6565+print "Number of particles after collision resolution:", simulate(PARTICLES, 50, collisions=True)[1]