~noelle/aoc-2021

5d35d8f6ac581480d7e4c4c841eb608014253cc2 — Noelle Leigh 2 years ago 6101bc1
08_2
2 files changed, 183 insertions(+), 0 deletions(-)

A 08/example.txt
A 08/puzzle_2.py
A 08/example.txt => 08/example.txt +1 -0
@@ 0,0 1,1 @@
acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf

A 08/puzzle_2.py => 08/puzzle_2.py +182 -0
@@ 0,0 1,182 @@
"""
Solution for AoC 2021 08 Puzzle

cat input.txt | python puzzle_2.py
"""
import sys

Digit = set[str]


def find_digits_with_segment_length(digits: list[Digit], segment_length: int):
    return list(filter(lambda digit: len(digit) == segment_length, digits))


def find_digit(digits: list[Digit], segment_length: int):
    possible_digits = find_digits_with_segment_length(digits, segment_length)
    if not possible_digits:
        raise ValueError(f"No digits found with segment length {segment_length}")
    elif len(possible_digits) > 1:
        raise ValueError(
            f"More than one digit found with segment length {segment_length}"
        )
    else:
        return possible_digits[0]


def find_1(digits: list[Digit]):
    return find_digit(digits, 2)


def find_4(digits: list[Digit]):
    return find_digit(digits, 4)


def find_7(digits: list[Digit]):
    return find_digit(digits, 3)


def find_8(digits: list[Digit]):
    return find_digit(digits, 7)


def find_segment_a(digits: list[Digit]):
    digit_1 = find_1(digits)
    digit_7 = find_7(digits)
    return (digit_7 - digit_1).pop()


def find_segments_b_d(digits: list[Digit]):
    digit_1 = find_1(digits)
    digit_4 = find_4(digits)
    return digit_4 - digit_1


def find_5(digits: list[Digit]):
    segments_b_d = find_segments_b_d(digits)
    return find_digit(filter(lambda digit: segments_b_d <= digit, digits), 5)


def find_segment_g(digits: list[Digit]):
    digit_5 = find_5(digits)
    digit_4 = find_4(digits)
    segment_a = find_segment_a(digits)
    return (digit_5 - digit_4 - {segment_a}).pop()


def find_9(digits: list[Digit]):
    digit_4 = find_4(digits)
    segment_a = find_segment_a(digits)
    segment_g = find_segment_g(digits)
    digits_6_segments = find_digits_with_segment_length(digits, 6)
    return next(
        filter(
            lambda digit: len(digit - digit_4 - {segment_a} - {segment_g}) == 0,
            digits_6_segments,
        )
    )


def find_segment_e(digits: list[Digit]):
    digit_8 = find_8(digits)
    digit_9 = find_9(digits)
    return (digit_8 - digit_9).pop()


def find_3(digits: list[Digit]):
    digit_9 = find_9(digits)
    digit_5 = find_5(digits)
    digits_5_segments = filter(
        lambda digit: digit != digit_5, find_digits_with_segment_length(digits, 5)
    )
    return next(filter(lambda digit: len(digit - digit_9) == 0, digits_5_segments))


def find_segment_d(digits: list[Digit]):
    segments_b_d = find_segments_b_d(digits)
    digit_3 = find_3(digits)
    return next(filter(lambda segment: segment in digit_3, segments_b_d))


def find_segment_b(digits: list[Digit]):
    segments_b_d = find_segments_b_d(digits)
    segment_d = find_segment_d(digits)
    return (segments_b_d - {segment_d}).pop()


def find_2(digits: list[Digit]):
    digit_5 = find_5(digits)
    digit_3 = find_3(digits)
    digits_5_segments = find_digits_with_segment_length(digits, 5)
    return next(
        filter(lambda digit: digit not in (digit_3, digit_5), digits_5_segments)
    )


def find_segment_c(digits: list[Digit]):
    digit_3 = find_3(digits)
    digit_5 = find_5(digits)
    return (digit_3 - digit_5).pop()


def find_segment_f(digits: list[Digit]):
    digit_7 = find_7(digits)
    segment_a = find_segment_a(digits)
    segment_c = find_segment_c(digits)
    return (digit_7 - {segment_a} - {segment_c}).pop()


def print_segments(digits: list[Digit]):
    a = find_segment_a(digits)
    b = find_segment_b(digits)
    c = find_segment_c(digits)
    d = find_segment_d(digits)
    e = find_segment_e(digits)
    f = find_segment_f(digits)
    g = find_segment_g(digits)

    print(f" {a * 4} ")
    print(f"{b}    {c}")
    print(f"{b}    {c}")
    print(f" {d * 4} ")
    print(f"{e}    {f}")
    print(f"{e}    {f}")
    print(f" {g * 4} ")


def find_decoded_digits(digits: list[Digit]) -> list[Digit]:
    a = find_segment_a(digits)
    b = find_segment_b(digits)
    c = find_segment_c(digits)
    d = find_segment_d(digits)
    e = find_segment_e(digits)
    f = find_segment_f(digits)
    g = find_segment_g(digits)

    return [
        {a, b, c, e, f, g},
        {c, f},
        {a, c, d, e, g},
        {a, c, d, f, g},
        {b, c, d, f},
        {a, b, d, f, g},
        {a, b, d, e, f, g},
        {a, c, f},
        {a, b, c, d, e, f, g},
        {a, b, c, d, f, g},
    ]


if __name__ == "__main__":
    output_sum = 0
    for line in sys.stdin:
        signal_patterns, output_value = map(
            lambda data: list(map(set, data.split())), line.split("|", 1)
        )
        decoded_digits = find_decoded_digits(signal_patterns)
        decoded_value = "".join(
            map(lambda digit: str(decoded_digits.index(digit)), output_value)
        )
        output_sum += int(decoded_value)

    sys.stdout.write(str(output_sum))