Last night's challenge for Advent of Code required coders to examine several input strings of parentheses and determine which ones were properly balanced but incomplete. My co-workers found this hilarious because this problem used to be one of our go-to interview questions for technical phone screens with candidates. Unsurprisingly, we all completed the problem significantly faster than usual!

To celebrate, I'm sharing my solution to both parts of the Day 10 challenge:

OPEN_TO_CLOSE = {
    "(": ")",
    "[": "]",
    "{": "}",
    "<": ">",
}

ERROR_POINTS = {
    ")": 3,
    "]": 57, 
    "}": 1197, 
    ">": 25137,
}

COMPLETION_POINTS = {
    ")": 1,
    "]": 2, 
    "}": 3, 
    ">": 4,
}


def points(line):
    stack = []
    for char in line:
        if char in OPEN_TO_CLOSE:
            stack.append(OPEN_TO_CLOSE[char])
        elif stack.pop() != char:
            return ERROR_POINTS[char], None

    total = 0
    for char in stack[::-1]:
        total *= 5
        total += COMPLETION_POINTS[char]
    return None, total


def error(line):
    return points(line)[0]


def completion(line):
    return points(line)[1]


def part_1(path):
    data = read_file(path)
    errors = [x for line in data if (x := error(line)) is not None]
    return sum(errors)


def part_2(path):
    data = read_file(path)
    lines = [x for line in data if (x := completion(line)) is not None]
    lines.sort()
    middle = len(lines)//2
    return lines[middle]