Day 14 - Advent of Code 2022

Working solutions for the day 14 puzzles.

Part One

""" day_14_01.py """

# usage: python3 day_14_01.py < input


import sys


def parse(data):
    """ calculate scan from input data """
    paths = [path.split(' -> ') for path in data.splitlines()]
    paths = [[list(map(int, pt.split(','))) for pt in path]
             for path in paths]

    output = {}
    for path in paths:
        for index, _ in enumerate(path[:-1]):
            x1, y1 = path[index]
            x2, y2 = path[index + 1]
            dx, dy = (x2 > x1) - (x2 < x1), (y2 > y1) - (y2 < y1)
            output[x1, y1] = '#'
            while (x1, y1) != (x2, y2):
                x1, y1 = x1 + dx, y1 + dy
                output[x1, y1] = '#'
    return output


def drop_sand(start, data):
    """ drop unit of sand from start in scan data """
    def check(pos, delta):
        """ get object at pos + delta """
        x, y = pos
        dx, dy = delta
        return data.get((x + dx, y + dy), '.')

    x, y = start
    while True:
        if y == data['limit']:
            return None
        if check((x, y), (0, 1)) == '.':
            y = y + 1
        elif check((x, y), (-1, 1)) == '.':
            x, y = x - 1, y + 1
        elif check((x, y), (1, 1)) == '.':
            x, y = x + 1, y + 1
        else:
            return x, y


scan = parse(sys.stdin.read())
scan['limit'] = max([y for _, y in scan])

units = 0
while (rest := drop_sand((500, 0), scan)) is not None:
    scan[rest] = 'O'
    units += 1
print(units)

Part Two

""" day_14_02.py """

# usage: python3 day_14_02.py < input


import sys


def parse(data):
    """ calculate scan from input data """
    paths = [path.split(' -> ') for path in data.splitlines()]
    paths = [[list(map(int, pt.split(','))) for pt in path]
             for path in paths]

    output = {}
    for path in paths:
        for index, _ in enumerate(path[:-1]):
            x1, y1 = path[index]
            x2, y2 = path[index + 1]
            dx, dy = (x2 > x1) - (x2 < x1), (y2 > y1) - (y2 < y1)
            output[x1, y1] = '#'
            while (x1, y1) != (x2, y2):
                x1, y1 = x1 + dx, y1 + dy
                output[x1, y1] = '#'
    return output


def drop_sand(start, data):
    """ drop unit of sand from start in scan data """
    def check(pos, delta):
        """ get object at pos + delta """
        x, y = pos
        dx, dy = delta
        if y + dy == data['limit'] + 2:
            return '#'
        return data.get((x + dx, y + dy), '.')

    x, y = start
    while True:
        if check((x, y), (0, 1)) == '.':
            y = y + 1
        elif check((x, y), (-1, 1)) == '.':
            x, y = x - 1, y + 1
        elif check((x, y), (1, 1)) == '.':
            x, y = x + 1, y + 1
        else:
            return x, y


scan = parse(sys.stdin.read())
scan['limit'] = max([y for _, y in scan])

units = 1
while (rest := drop_sand((500, 0), scan)) != (500, 0):
    scan[rest] = 'O'
    units += 1
print(units)