# --- Day 5: Hydrothermal Venture --- # You come across a field of hydrothermal vents on the ocean floor! These vents # constantly produce large, opaque clouds, so it would be best to avoid them if # possible. # They tend to form in lines; the submarine helpfully produces a list of nearby # lines of vents (your puzzle input) for you to review. For example: # 0,9 -> 5,9 # 8,0 -> 0,8 # 9,4 -> 3,4 # 2,2 -> 2,1 # 7,0 -> 7,4 # 6,4 -> 2,0 # 0,9 -> 2,9 # 3,4 -> 1,4 # 0,0 -> 8,8 # 5,5 -> 8,2 # Each line of vents is given as a line segment in the format x1,y1 -> x2,y2 # where x1,y1 are the coordinates of one end the line segment and x2,y2 are the # coordinates of the other end. These line segments include the points at both # ends. In other words: # An entry like 1,1 -> 1,3 covers points 1,1, 1,2, and 1,3. # An entry like 9,7 -> 7,7 covers points 9,7, 8,7, and 7,7. # For now, only consider horizontal and vertical lines: lines where either # x1 = x2 or y1 = y2. # So, the horizontal and vertical lines from the above list would produce the # following diagram: # .......1.. # ..1....1.. # ..1....1.. # .......1.. # .112111211 # .......... # .......... # .......... # .......... # 222111.... # In this diagram, the top left corner is 0,0 and the bottom right corner is # 9,9. Each position is shown as the number of lines which cover that point or # . if no line covers that point. The top-left pair of 1s, for example, comes # from 2,2 -> 2,1; the very bottom row is formed by the overlapping lines 0,9 # -> 5,9 and 0,9 -> 2,9. # To avoid the most dangerous areas, you need to determine the number of points # where at least two lines overlap. In the above example, this is anywhere in # the diagram with a 2 or larger - a total of 5 points. # Consider only horizontal and vertical lines. At how many points do at least # two lines overlap? from collections import defaultdict from typing import DefaultDict, Tuple with open("files/P5.txt") as f: points = [ points for line in f.read().strip().split("\n") for points in line.split(" -> ") ] point_1 = points[::2] x1y1 = [int(val) for p in point_1 for val in p.split(",")] x_1 = x1y1[::2] y_1 = x1y1[1::2] point_2 = points[1::2] x2y2 = [int(val) for p in point_2 for val in p.split(",")] x_2 = x2y2[::2] y_2 = x2y2[1::2] def get_range(p1: int, p2: int) -> Tuple[int, int, int]: if p1 > p2: step = -1 p2 -= 1 else: step = 1 p2 += 1 return p1, p2, step def part_1() -> None: grid: DefaultDict[Tuple[int, int], int] = defaultdict(int) for idx in range(len(points) // 2): if x_1[idx] == x_2[idx]: start, stop, step = get_range(y_1[idx], y_2[idx]) for pos in range(start, stop, step): grid[x_1[idx], pos] += 1 elif y_1[idx] == y_2[idx]: start, stop, step = get_range(x_1[idx], x_2[idx]) for pos in range(start, stop, step): grid[pos, y_1[idx]] += 1 print(sum(1 for key in grid.values() if key > 1)) # --- Part Two --- # Unfortunately, considering only horizontal and vertical lines doesn't give # you the full picture; you need to also consider diagonal lines. # Because of the limits of the hydrothermal vent mapping system, the lines in # your list will only ever be horizontal, vertical, or a diagonal line at # exactly 45 degrees. In other words: # An entry like 1,1 -> 3,3 covers points 1,1, 2,2, and 3,3. # An entry like 9,7 -> 7,9 covers points 9,7, 8,8, and 7,9. # Considering all lines from the above example would now produce the following # diagram: # 1.1....11. # .111...2.. # ..2.1.111. # ...1.2.2.. # .112313211 # ...1.2.... # ..1...1... # .1.....1.. # 1.......1. # 222111.... # You still need to determine the number of points where at least two lines # overlap. In the above example, this is still anywhere in the diagram with a 2 # or larger - now a total of 12 points. # Consider all of the lines. At how many points do at least two lines overlap? def part_2() -> None: grid: DefaultDict[Tuple[int, int], int] = defaultdict(int) for idx in range(len(points) // 2): # print(idx) if x_1[idx] == x_2[idx]: start, stop, step = get_range(y_1[idx], y_2[idx]) for pos in range(start, stop, step): grid[x_1[idx], pos] += 1 elif y_1[idx] == y_2[idx]: start, stop, step = get_range(x_1[idx], x_2[idx]) for pos in range(start, stop, step): grid[pos, y_1[idx]] += 1 elif abs(x_1[idx] - x_2[idx]) == abs(y_1[idx] - y_2[idx]): start_x, stop_x, step_x = get_range(x_1[idx], x_2[idx]) start_y, stop_y, step_y = get_range(y_1[idx], y_2[idx]) pos_x = [pos_x for pos_x in range(start_x, stop_x, step_x)] pos_y = [pos_y for pos_y in range(start_y, stop_y, step_y)] for px, py in zip(pos_x, pos_y): grid[px, py] += 1 print(sum(1 for key in grid.values() if key > 1)) if __name__ == "__main__": part_1() part_2()