Advent of Code: Day 8

Author: Leo Nguyen Dec 8, 2021

Oh man, Day 8 is hard.

Part 1

You're given a seven-segment display. That's the numbers on your digital clock, each one formed by some segments. However, the segments are all mixed up, so you'll need to demix them somehow.

For the input, you're given the list of all digits that are now represented by the mixed up segments. However, the mixup itself is consistent for all digits. Now if we pay careful attention, we will realize that the digits 1, 4, 7, and 8 each has a unique number of segments. So for Part 1, calculate how many times these 4 digits appear.

It's just a simple counting problem, so here's my Python code:

def read(filename):
    file = open(filename)
    lines = file.read().splitlines()

    entries = []

    for line in lines:
        fields = line.split(' | ')
        left = fields[0]
        right = fields[1]
        segments = left.split()
        digits = right.split()
        entries.append([segments, digits])

    return entries


def solve(entries):
    # Count unique digits
    count = 0

    for entry in entries:
        digits = entry[1]
        for digit in digits:
            if len(digit) in [2, 4, 3, 7]:
                count += 1
                print(digit)

    print(count)


entries = read('input.txt')
solve(entries)

Part 2: The Real Monster

After Part 1, the real problem appears. In the input, you're also given a 4-digit number for each line. Of course, each has segments mixed up like before. Now, use analysis to decode these numbers then add them up.

This took me one full day to complete, not gonna lie.

After a lot of thinking, I found that we could reuse part of Part 1. Basically, now we know what 1, 4, 7, 8 look like. Notice that if we take the segments of 7 and subtract 1's segments, we now get the segment a left. And that's our first segment decoded!

Next, for each entry, if we count the number of times a segment appears in the digits from 0 to 9, we will have these unique situations:

  • Segment b appears exactly 6 times

  • Segment e appears exactly 4 times

  • Segment f appears exactly 9 times

So this gives us another 3 segments decoded. Also, some pairs of segments appear the same number of times too:

  • Segments a and c appear exactly 8 times

  • Segments d and g appear exactly 7 times

For ac, since we already knew a from before, we can easily deduct segment c.

Now, if we subtract 7's segments from 4's, we have two segments of bd. Coupled with the segments dg above, we can find the only segment d that satisfies two criteria. The only segment left, g must be the remaining one.

As usual, below is my solution code. Please excuse its length:

def read(filename):
    file = open(filename)
    lines = file.read().splitlines()

    entries = []

    for line in lines:
        fields = line.split(' | ')
        left = fields[0]
        right = fields[1]
        digits = left.split()
        outputs = right.split()
        entries.append([digits, outputs])

    return entries


def parse_digit(dictionary, mixed_digit):
    a = dict()
    a['abcefg'] = 0
    a['cf'] = 1
    a['acdeg'] = 2
    a['acdfg'] = 3
    a['bcdf'] = 4
    a['abdfg'] = 5
    a['abdefg'] = 6
    a['acf'] = 7
    a['abcdefg'] = 8
    a['abcdfg'] = 9

    segments = [dictionary[mixed_segment] for mixed_segment in mixed_digit]
    segments.sort()
    return a[''.join(segments)]


def solve(entries):
    # create dictionary(a -> b) and digits([0] = 'ab')
    dictionary = dict()
    digits = [''] * 10

    total = 0

    for entry in entries:
        mixed_digits, outputs = entry

        # Determine unique digits
        digits[1] = [digit for digit in mixed_digits if len(digit) == 2][0]
        digits[7] = [digit for digit in mixed_digits if len(digit) == 3][0]
        digits[4] = [digit for digit in mixed_digits if len(digit) == 4][0]
        digits[8] = [digit for digit in mixed_digits if len(digit) == 7][0]

        # Determine segment a from 7 - 1
        a = list(set(digits[7]) - set(digits[1]))[0]
        dictionary[a] = 'a'

        # Count characters
        character_count = dict()

        for char in 'abcdefg':
            character_count[char] = 0

        for digit in mixed_digits:
            for char in digit:
                character_count[char] += 1

        # Determine character maps
        ac, dg = [], []
        for char in 'abcdefg':
            match character_count[char]:
                case 6:
                    dictionary[char] = 'b'
                case 4:
                    dictionary[char] = 'e'
                case 9:
                    dictionary[char] = 'f'
                case 8:  # could be either a or c
                    ac.append(char)
                case 7:  # could be either d or g
                    dg.append(char)

        # Determine a and c
        # c = ac - a
        dictionary[list(set(ac) - set(a))[0]] = 'c'

        # Determine d and g
        # d exists in bd = 4 - 1
        bd = set(digits[4]) - set(digits[1])
        if dg[0] in bd:
            dictionary[dg[0]] = 'd'
            dictionary[dg[1]] = 'g'
        else:
            dictionary[dg[1]] = 'd'
            dictionary[dg[0]] = 'g'

        # Parse mixed digits to digits
        parsed_number = [parse_digit(dictionary, mixed_digit) for mixed_digit in outputs]

        s = parsed_number[0] * 1000 + parsed_number[1] * 100 + parsed_number[2] * 10 + parsed_number[3]
        total += s

    print(total)


entries = read('input.txt')
solve(entries)