Day 16 - Advent of Code 2023

Working solutions for the day 16 puzzles.

Part One

""" day_16_01.py """

# usage: python3 day_16_01.py < input

import sys


data = sys.stdin.read()
width = data.index('\n')
height = len(data) // (width + 1)

device = [i for i in data if i != '\n']


def exited(u):
    """ left device """
    return not (0 <= u[0] < height and 0 <= u[1] < width)


visited = []
beams = [[(0, -1), (0, 1)]]
while beams:
    add = []
    delete = []
    for i, (pos, heading) in enumerate(beams):
        next_pos = pos[0] + heading[0], pos[1] + heading[1]
        if exited(next_pos) or [next_pos, heading] in visited:
            delete.append(i)
            continue

        visited.append([next_pos, heading])
        beams[i][0] = next_pos

        tile = device[next_pos[0] * width + next_pos[1]]

        if tile == '|' and heading[0] == 0:
            beams[i][1] = (-1, 0)
            add.append([next_pos, (1, 0)])
        if tile == '-' and heading[1] == 0:
            beams[i][1] = (0, -1)
            add.append([next_pos, (0, 1)])
        if tile == '/':
            beams[i][1] = -heading[1], -heading[0]
        if tile == '\\':
            beams[i][1] = heading[1], heading[0]

    beams = [beam for i, beam in enumerate(beams) if i not in delete]
    beams.extend(add)

print(len({pos for pos, _ in visited}))

Part Two

""" day_16_02.py """

# usage: python3 day_16_02.py < input

import sys


data = sys.stdin.read()
width = data.index('\n')
height = len(data) // (width + 1)

device = [i for i in data if i != '\n']


def exited(u):
    """ left device """
    return not (0 <= u[0] < height and 0 <= u[1] < width)


entries = [[(-1, i), (1, 0)] for i in range(width)] + \
          [[(i, width), (0, -1)] for i in range(height)] + \
          [[(height, i), (-1, 0)] for i in range(width)] + \
          [[(i, -1), (0, 1)] for i in range(height)]

answer = 0
for entry in entries:
    visited = []
    beams = [entry]

    while beams:
        add = []
        delete = []
        for i, (pos, heading) in enumerate(beams):
            next_pos = pos[0] + heading[0], pos[1] + heading[1]
            if exited(next_pos) or [next_pos, heading] in visited:
                delete.append(i)
                continue

            visited.append([next_pos, heading])
            beams[i][0] = next_pos

            tile = device[next_pos[0] * width + next_pos[1]]

            if tile == '|' and heading[0] == 0:
                beams[i][1] = (-1, 0)
                add.append([next_pos, (1, 0)])
            if tile == '-' and heading[1] == 0:
                beams[i][1] = (0, -1)
                add.append([next_pos, (0, 1)])
            if tile == '/':
                beams[i][1] = -heading[1], -heading[0]
            if tile == '\\':
                beams[i][1] = heading[1], heading[0]

        beams = [beam for i, beam in enumerate(beams) if i not in delete]
        beams.extend(add)

    answer = max(answer, len({pos for pos, _ in visited}))

print(answer)