mirror of
https://github.com/thib8956/advent-of-code.git
synced 2025-08-23 16:01:59 +00:00
2019 day 10
This commit is contained in:
114
adventofcode/2019/day10/day10.py
Normal file
114
adventofcode/2019/day10/day10.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import time
|
||||
import math
|
||||
import cmath
|
||||
from itertools import combinations
|
||||
from collections import defaultdict
|
||||
from bisect import insort
|
||||
|
||||
|
||||
def dist(a, b):
|
||||
"euclidean distance between two points (as complex numbers)"
|
||||
return abs(a - b)
|
||||
|
||||
|
||||
def main(inp):
|
||||
asteroids = set()
|
||||
for y, l in enumerate(inp):
|
||||
for x, c in enumerate(l):
|
||||
if c == "#":
|
||||
asteroids.add(complex(x,y))
|
||||
base = part1(asteroids)
|
||||
#base = 22+28j
|
||||
|
||||
# construct a list of asteroids for each angle, sorted by distance from the base
|
||||
angles = defaultdict(list)
|
||||
for a in asteroids:
|
||||
if a == base:
|
||||
continue
|
||||
angle = (math.degrees(cmath.phase(a - base)) + 90) % 360
|
||||
elt = (abs(a - base), a)
|
||||
insort(angles[angle], elt)
|
||||
|
||||
i = 0
|
||||
for angle in sorted(angles):
|
||||
i += 1
|
||||
a = angles[angle].pop(0)
|
||||
if i == 200:
|
||||
_, a = a
|
||||
print("Part 2: ", int(a.real*100+a.imag))
|
||||
break
|
||||
|
||||
|
||||
def part1(asteroids):
|
||||
"""
|
||||
# ray cast solution not working
|
||||
start_time = time.perf_counter()
|
||||
sight = defaultdict(set)
|
||||
for a, b in combinations(asteroids, 2):
|
||||
if has_direct_sight_ray(a, b, asteroids):
|
||||
sight[a].add(b)
|
||||
sight[b].add(a)
|
||||
end_time = time.perf_counter() - start_time
|
||||
print(f"compute lines of sight ray {end_time:.4f}")
|
||||
m = max(sight.items(), key=lambda x: len(x[1]))
|
||||
print(m[0], len(m[1]))
|
||||
"""
|
||||
#start_time = time.perf_counter()
|
||||
# construct line of sight for each asteroid
|
||||
sight = defaultdict(set)
|
||||
for a, b in combinations(asteroids, 2):
|
||||
if has_direct_sight(a, b, asteroids):
|
||||
sight[a].add(b)
|
||||
sight[b].add(a)
|
||||
#end_time = time.perf_counter() - start_time
|
||||
#print(f"compute lines of sight {end_time:.4f}")
|
||||
|
||||
m = max(sight.items(), key=lambda x: len(x[1]))
|
||||
#print(m[0], len(m[1]))
|
||||
print("Part 1: ", len(m[1]))
|
||||
return m[0]
|
||||
|
||||
|
||||
def has_direct_sight(a, c, asteroids):
|
||||
"""
|
||||
Returns true if a can see c, i.e. they have no other asteroid between
|
||||
them. An asteroid b is between a and c if:
|
||||
dist(a,c) = dist(a,b) + dist(b,c).
|
||||
"""
|
||||
ac = dist(a, c)
|
||||
eps = 0.00001 # avoid floating-point shenanigans
|
||||
for b in asteroids: # check if any asteroid is blocking the view between a and c
|
||||
if b == a or b == c:
|
||||
continue
|
||||
if abs(dist(a, b) + dist(b, c) - ac) <= eps:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def has_direct_sight_ray(a, c, asteroids):
|
||||
# FIXME not working...
|
||||
"""
|
||||
Cast a ray from a in the direction of c and return true if there is no
|
||||
obstacle between a and c
|
||||
"""
|
||||
if c.real < a.real:
|
||||
a, c = c, a
|
||||
angle = cmath.phase(c - a)
|
||||
t = 0
|
||||
x = a.real
|
||||
y = a.imag
|
||||
while True:
|
||||
x = a.real + t * math.cos(angle)
|
||||
y = a.imag + t * math.sin(angle)
|
||||
t += 1
|
||||
if x >= c.real or y >= c.imag:
|
||||
return True
|
||||
if complex(int(x), int(y)) in asteroids:
|
||||
return False
|
||||
assert False, "Unreachable"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import fileinput
|
||||
main(fileinput.input())
|
||||
|
Reference in New Issue
Block a user