Add solution to day 10 part 2

master
Nick Krichevsky 2019-12-10 16:28:24 -05:00
parent 0bd38fd546
commit 4a62f46f28
1 changed files with 73 additions and 25 deletions

View File

@ -1,12 +1,14 @@
import sys
import copy
import fractions
import math
from typing import Tuple, Set
from typing import List, Iterable, Tuple
ASTEROID_CHAR = '#'
SPACE_CHAR = '.'
# Get the coordinates of all items in a line
def get_items_in_slope(space_map: Tuple[Tuple[str]], start_row: int, start_col: int, rise: int, run: int) -> Tuple[Tuple[int, int]]:
row = start_row
col = start_col
@ -24,24 +26,34 @@ def get_items_in_slope(space_map: Tuple[Tuple[str]], start_row: int, start_col:
return tuple(items)
def search_for_visible_in_all_directions(space_map: Tuple[Tuple[str, ...]], start_row: int, start_col: int, rise: int, run: int) -> Set[Tuple[int, int]]:
asteroids = set()
for i in (-1, 1):
for j in (-1, 1):
line_items = get_items_in_slope(space_map, start_row, start_col, i * rise, j * run)
for row, col in line_items:
if space_map[row][col] == ASTEROID_CHAR:
asteroids.add((row, col))
break
# Spread a set of coordinates into all quadrants (e.g. (1,1) will produce (1,1) (1,-1) (-1,1) (-1,-1)
def put_items_in_all_quadrants(items: Iterable[Tuple[int, int]]) -> List[Tuple[int, int]]:
mirrored = set()
for item in items:
for i in (-1, 1):
for j in (-1, 1):
mirrored.add((i * item[0], j * item[1]))
return asteroids
return list(mirrored)
def get_visible_asteroid_count(space_map: Tuple[Tuple[str, ...]], start_row: int, start_col: int) -> int:
asteroids = set()
# Start a list of slopes clockwise, starting at pi/2
def sort_slopes_clockwise(items: List[Tuple[int, int]]) -> List[Tuple[int, int]]:
# math.atan(-x[0]/abs(x[0])) allows us to get the correct positive/negative infinity for arctan
# (remember, our coords are flipped from a standard coordinate plane)
sort_key = lambda x: math.atan(-x[0]/abs(x[0]) * math.inf if x[1] == 0 else -x[0]/x[1])
quad_1_3_items = sorted((item for item in items if item[1] >= 0), reverse=True, key=sort_key)
quad_2_4_items = sorted((item for item in items if item[1] < 0), reverse=True, key=sort_key)
return quad_1_3_items + quad_2_4_items
def generate_all_slopes(height: int, width: int) -> Tuple[Tuple[int, int]]:
# Get all slopes in the first quadrant
quad_slopes = set()
used_slopes = set()
for rise in range(len(space_map)):
for run in range(len(space_map[0])):
for rise in range(height):
for run in range(width):
effective_rise, effective_run = rise, run
if rise == 0 and run == 0:
continue
@ -55,23 +67,58 @@ def get_visible_asteroid_count(space_map: Tuple[Tuple[str, ...]], start_row: int
continue
used_slopes.add(slope_fraction)
found_asteroids = search_for_visible_in_all_directions(space_map, start_row, start_col, effective_rise, effective_run)
asteroids = asteroids.union(found_asteroids)
quad_slopes.add((effective_rise, effective_run))
return len(asteroids)
# Spread them across the rest of the coordinates
slopes = put_items_in_all_quadrants(quad_slopes)
return sort_slopes_clockwise(slopes)
def part1(space_map: Tuple[Tuple[int, ...]]):
# Get all asteroids that are visible from a certian point, marking whether to destroy them on scan or not
def get_visible_asteroids(space_map: List[List[int]], start_row: int, start_col: int, destroy=False) -> List[Tuple[int, int]]:
asteroids = []
slopes = generate_all_slopes(len(space_map), len(space_map[0]))
for slope in slopes:
rise, run = slope
line_items = get_items_in_slope(space_map, start_row, start_col, rise, run)
for row, col in line_items:
if space_map[row][col] == ASTEROID_CHAR:
if destroy:
space_map[row][col] = SPACE_CHAR
if (row, col) not in asteroids:
asteroids.append((row, col))
break
return asteroids
def part1(input_space_map: List[List[str]]) -> Tuple[Tuple[int, int], int]:
space_map = copy.deepcopy(input_space_map)
best_count = None
best_pos = None
for i, row in enumerate(space_map):
for j, item in enumerate(row):
if item != ASTEROID_CHAR:
continue
visible_asteroid_count = get_visible_asteroid_count(space_map, i, j)
visible_asteroid_count = len(get_visible_asteroids(space_map, i, j))
if best_count is None or visible_asteroid_count > best_count:
best_pos = (i, j)
best_count = visible_asteroid_count
return best_count
return best_pos, best_count
def part2(input_space_map: Tuple[Tuple[str, ...]], station_pos: Tuple[int, int]) -> int:
space_map = copy.deepcopy(input_space_map)
destroyed_asteroids = []
station_row, station_col = station_pos
while len(destroyed_asteroids) < 200:
destroyed_asteroids += get_visible_asteroids(space_map, station_row, station_col, True)
asteroid = destroyed_asteroids[199]
return asteroid[1] * 100 + asteroid[0]
if __name__ == "__main__":
@ -80,10 +127,11 @@ if __name__ == "__main__":
sys.exit(1)
with open(sys.argv[1]) as f:
inputs = tuple(tuple(line.strip()) for line in f.readlines())
inputs = [list(line.strip()) for line in f.readlines()]
for row in inputs:
print(''.join(row))
# print(get_visible_asteroid_count(inputs, 0, 1))
print(part1(inputs))
best_pos, best_count = part1(inputs)
print(best_count)
print(part2(inputs, best_pos))