Day 15 - Advent of Code 2022

Working solutions for the day 15 puzzles.

Part One

""" day_15_01.py """

# usage: python3 day_15_01.py < input


import sys


def parse(data):
    """ parse sensor data """
    output = [line.removeprefix('Sensor at ') for line in data.splitlines()]
    output = [line.replace(': closest beacon is at ', ', ') for line in output]
    output = [[int(term[2:]) for term in line.split(', ')] for line in output]
    parsed_data = {(x1, y1): (x2, y2) for x1, y1, x2, y2 in output}
    return parsed_data


def manhattan(begin, end):
    """ calculate manhattan distance from begin to end """
    x1, y1 = begin
    x2, y2 = end
    return abs(x1 - x2) + abs(y1 - y2)


def in_range(target, _sensor, reach):
    """ is target in sensor reach """
    if manhattan(_sensor, (_sensor[0], target)) > reach:
        return False
    return True


def leftmost(target, data):
    """ calculate leftmost point scanned affecting target row """
    output = float('inf')
    for _sensor in data:
        reach = manhattan(_sensor, data[_sensor])
        if not in_range(target, _sensor, reach):
            continue
        output = min(output, _sensor[0] - reach)
    return output


def rightmost(target, data):
    """ calculate rightmost point scanned affecting target row """
    output = float('-inf')
    for _sensor in data:
        reach = manhattan(_sensor, data[_sensor])
        if not in_range(target, _sensor, reach):
            continue
        output = max(output, _sensor[0] + reach)
    return output


report = parse(sys.stdin.read())
objects = set(report.keys()) | set(report.values())
manhattans = {sensor: manhattan(sensor, report[sensor]) for sensor in report}

y = 2000000
left, right = leftmost(y, report), rightmost(y, report)

count = 0
for x in range(left, right + 1):
    if (x, y) in objects:
        continue
    possible = True
    for sensor in report:
        distance = manhattan(sensor, (x, y))
        if distance <= manhattans[sensor]:
            possible = False
    if not possible:
        count += 1
print(count)

Part Two

""" day_15_02.py """

# usage: python3 day_15_02.py < input


import sys


def parse(data):
    """ parse sensor data """
    output = [line.removeprefix('Sensor at ') for line in data.splitlines()]
    output = [line.replace(': closest beacon is at ', ', ') for line in output]
    output = [[int(term[2:]) for term in line.split(', ')] for line in output]
    parsed_data = {(x1, y1): (x2, y2) for x1, y1, x2, y2 in output}
    return parsed_data


def manhattan(begin, end):
    """ calculate manhattan distance from begin to end """
    x1, y1 = begin
    x2, y2 = end
    return abs(x1 - x2) + abs(y1 - y2)


def outline(origin, radius, boundary):
    """ calculate points radius + 1 away from origin within boundary """
    def valid(coordinate):
        """ determine if coordinate is within boundary """
        x, y = coordinate
        if any([x < 0, x > boundary, y < 0, y > boundary]):
            return False
        return True

    output = []
    dx, dy = 1, 1
    point = origin[0], origin[1] - (radius + 1)
    while point[1] < origin[1]:
        if valid(point):
            output.append(point)
        point = point[0] + dx, point[1] + dy
    dx, dy = -1, 1
    point = origin[0] + radius + 1, origin[1]
    while point[0] > origin[0]:
        if valid(point):
            output.append(point)
        point = point[0] + dx, point[1] + dy
    dx, dy = -1, -1
    point = origin[0], origin[1] + radius + 1
    while point[1] > origin[1]:
        if valid(point):
            output.append(point)
        point = point[0] + dx, point[1] + dy
    dx, dy = 1, -1
    point = origin[0] - (radius + 1), origin[1]
    while point[0] < origin[0]:
        if valid(point):
            output.append(point)
        point = point[0] + dx, point[1] + dy
    return output


report = parse(sys.stdin.read())
manhattans = {sensor: manhattan(sensor, report[sensor]) for sensor in report}

limit = 4000000

px, py = None, None
loop = True
for sensor in report:
    points = outline(sensor, manhattans[sensor], limit)
    for px, py in points:
        for another in report:
            if another != sensor:
                if manhattan(another, (px, py)) <= manhattans[another]:
                    break
        else:
            loop = False
            break
    if not loop:
        break
print(px * 4000000 + py)