···11+import math
22+import fileinput
33+from collections import deque, defaultdict
44+55+66+def dist(x, y):
77+ return (((x[0] - y[0]) ** 2) + ((x[1] - y[1]) ** 2)) ** 0.5
88+99+1010+def angle(a, b):
1111+ return math.atan2(a[1] - b[1], a[0] - b[0])
1212+1313+1414+EPS = 1e-6
1515+1616+# Read problem input
1717+grid = []
1818+asteroids = set()
1919+2020+for y, line in enumerate(fileinput.input()):
2121+ for x, c in enumerate(line.strip()):
2222+ if c == '#':
2323+ asteroids.add((x, y))
2424+ grid.append([c == '#' for c in line.strip()])
2525+2626+width = len(grid[0])
2727+height = len(grid)
2828+2929+# Part 1
3030+detections = {}
3131+3232+for y in range(height):
3333+ for x in range(width):
3434+ if not grid[y][x]:
3535+ continue
3636+3737+ p = (x, y)
3838+ count = 0
3939+ seen_angles = set()
4040+4141+ for other in sorted(asteroids, key=lambda a: dist(p, a))[1:]:
4242+ if angle(p, other) not in seen_angles:
4343+ seen_angles.add(angle(p, other))
4444+ count += 1
4545+4646+ detections[x, y] = count
4747+4848+station, num = max(detections.items(), key=lambda x: x[1])
4949+print "Number of detections:", num
5050+5151+5252+# Part 2
5353+asteroids.remove(station)
5454+5555+# Construct a circular list that is sorted by increasing angle,
5656+# and contains sorted lists of the asteroids at that angle from
5757+# the station (going from furthest to closest). We rotate in the
5858+# positive-radian direction as a hack, since the y-axis coordinates
5959+# actually increase going downwards (as opposed to upwards).
6060+by_angles = defaultdict(list)
6161+for a in asteroids:
6262+ by_angles[angle(station, a)].append(a)
6363+6464+for k, v in by_angles.items():
6565+ v.sort(key=lambda a: -dist(station, a))
6666+6767+queue = deque(sorted(by_angles.items()))
6868+6969+# Rotate the queue to the starting angle
7070+start = math.pi / 2
7171+while abs(queue[0][0] - start) > EPS:
7272+ queue.rotate(-1)
7373+7474+# Iterate through the circular list, vaporizing the closest asteroid
7575+# at that angle, then moving to the next angle in the rotation.
7676+for i in range(200):
7777+ at_angle = queue[0][1]
7878+ vaporized = at_angle.pop()
7979+ if not at_angle:
8080+ queue.popleft()
8181+ else:
8282+ queue.rotate(-1)
8383+8484+print "200th asteroid checksum:", vaporized[0] * 100 + vaporized[1]