Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2021-12-08 - Seven Segment Search

(original .ipynb)

Day 8 puzzle input is a collection of: 1. patterns that represent all digits 0-9 in a random order in a 7-digit display followed by a pipe ... 2. then a pattern representing four digits that comprise an output

The segments in the display are addressed as a through g and the digits are constructed like the below:

   0:      1:      2:      3:      4:
  aaaa    ....    aaaa    aaaa    ....
 b    c  .    c  .    c  .    c  b    c
 b    c  .    c  .    c  .    c  b    c
  ....    ....    dddd    dddd    dddd
 e    f  .    f  e    .  .    f  .    f
 e    f  .    f  e    .  .    f  .    f
  gggg    ....    gggg    gggg    ....

   5:      6:      7:      8:      9:
  aaaa    aaaa    aaaa    aaaa    aaaa
 b    .  b    .  .    c  b    c  b    c
 b    .  b    .  .    c  b    c  b    c
  dddd    dddd    ....    dddd    dddd
 .    f  e    f  .    f  e    f  .    f
 .    f  e    f  .    f  e    f  .    f
  gggg    gggg    ....    gggg    gggg

My puzzle input is here. Part 1 involves finding the number of times the digits 1, 4, 7 and 8 (which are easy and unambiguous to spot) appear in the output. Part 2 involves finding a way to decode each line sum the resulting outputs.

puzzle_input_str = open("puzzle_input/day8.txt").read()

test_input_str = """be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe
edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc
fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg
fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb
aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea
fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb
dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe
bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef
egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb
gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce"""

digits = [
    "abcefg",
    "cf",
    "acdeg",
    "acdfg",
    "bcdf",
    "abdfg",
    "abdefg",
    "acf",
    "abcdefg",
    "abcdfg"
]

to_digits = {pattern:i for i, pattern in enumerate(digits)}


def parse_line(line):
    input_values_str, output_values_str = line.split(" | ")
    return (
        input_values_str.split(" "),
        output_values_str.split(" ")
    ) 


def parse_input_lines(input_str):
    return [parse_line(line) for line in input_str.split("\n")]


def is_1478(v):
    v_len = len(v)
    return v_len == len(digits[1]) or v_len == len(digits[4]) or v_len == len(digits[7]) or v_len == len(digits[8])


def part_one(input_str):
    count = 0
    for inputs, outputs in parse_input_lines(input_str):
        count += sum(1 for o in outputs if is_1478(o))
        
    return count


assert 26 == part_one(test_input_str)
print("part one:", part_one(puzzle_input_str))
part one: 476
def find_known(input_values, n):
    return [set(v) for v in input_values if len(v) == len(digits[n])][0]


def find_with_len(input_values, l):
    return [set(v) for v in input_values if len(v) == l]


def find_with_segments(input_values, segments):
    return [set(v) for v in input_values if segments.issubset(set(v))]

# get the value contained within a set with 1 item
def single(some_set):
    assert len(some_set) == 1
    return next(iter(some_set))


def find_a(input_values, signal_map):
    one = find_known(input_values, 1)
    seven = find_known(input_values, 7)
    signal_map["a"] = single(seven.difference(one))
    

def find_g(input_values, signal_map):
    modified_four = find_known(input_values, 4)
    modified_four.add(signal_map["a"])
    
    maybe_nines = find_with_len(input_values, len(digits[9]))
    nine = find_with_segments(maybe_nines, modified_four)[0]
    signal_map["g"] = single(nine.difference(modified_four))

    
def find_c_d_f(input_values, signal_map):
    # construct a 9
    nine = find_known(input_values, 4)
    nine.add(signal_map["a"])
    nine.add(signal_map["g"])
    
    # zs1, zs2 are either zero or six
    zs1, zs2 = [n for n in find_with_len(input_values, len(nine)) if n != nine]
    
    c_and_d = zs1.difference(zs2).union(zs2.difference(zs1))
    one = find_known(input_values, 1)
    c = single(one.intersection(c_and_d))
    signal_map["c"] = c
    
    c_and_d.discard(c)
    d = single(c_and_d)
    signal_map["d"] = d
    
    one.discard(c)
    one.discard(d)
    signal_map["f"] = single(one)

    
def find_b(input_values, signal_map):
    four = find_known(input_values, 4)
    
    four.discard(signal_map["c"])
    four.discard(signal_map["d"])
    four.discard(signal_map["f"])
    
    signal_map["b"] = single(four)
    
    
def find_e(input_values, signal_map):
    # its early im tired this is stupid but whatever
    eight = find_known(input_values, 8)
    
    for char in "abcdfg":
        eight.discard(signal_map[char])
    
    signal_map["e"] = single(eight)


def get_signal_map(input_values):
    signal_map = {}
    
    for f in (find_a, find_g, find_c_d_f, find_b, find_e):
        f(input_values, signal_map)
    
    # fuck its the wrong way round
    return {v:k for k,v in signal_map.items()}


def decode(output, signal_map):
    return "".join(sorted(signal_map[c] for c in output))
    
    
def part_two(input_str):
    count = 0
    for inputs, outputs in parse_input_lines(input_str):
        signal_map = get_signal_map(inputs)
        decoded = "".join(str(to_digits[decode(output, signal_map)]) for output in outputs)
        count += int(decoded)
        
    return count
    

assert 61229 == part_two(test_input_str)
print("part two:", part_two(puzzle_input_str))
part two: 1011823