Files
adventofcode-2021/day08/day08.py
Alek Ratzloff 3a4d744b3c Add day 08 (phew)
This could probably be better, but I don't really care because that one
was tough.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2021-12-09 17:50:20 -08:00

157 lines
4.9 KiB
Python
Executable File

#!/usr/bin/env python3
import sys
from typing import DefaultDict, FrozenSet, List, Sequence, Tuple
from collections import defaultdict
import pprint
def setstr(s: FrozenSet[str]) -> str:
return "".join(sorted(s))
def part1(lines: Sequence[Tuple[Sequence[str], Sequence[str]]]):
# Lazy solution
lengths: DefaultDict[int, int] = defaultdict(lambda: 0)
for _, digits in lines:
for digit in digits:
lengths[len(digit)] += 1
# 1, 4, 7, and 8 use a unique number of digits.
print(f"1: {lengths[2]}")
print(f"4: {lengths[4]}")
print(f"7: {lengths[3]}")
print(f"8: {lengths[7]}")
print(f"Sum: {sum(map(lengths.get, (2, 4, 3, 7)))}")
def part2(lines: Sequence[Tuple[Sequence[str], Sequence[str]]]):
# This is a table of 0-9 on the 7-seg display based on which lines are used:
#
# We can use this to figure out digits:
# 1: EASY - Has 2 segments
# 4: EASY - Has 4 segments
# 7: EASY - Has 3 segments
# 8: EASY - Has 7 segments
# 2: Uniquely shares the C segment with 1
# 9: Shares all segments with 4, and also has two more
# 3: Subtract 2 from 8, and subtract that result from 9
# 6: Subtract C segment from 8
# 5: Subtract C and E segments from 8
# 0: Last remaining that we haven't figured out yet, no tricks here
nums = []
for patterns_, digits in lines:
# using the name patterns_ so re-assignment isn't an issue thanks to mypy :|
patterns = [frozenset(pat) for pat in patterns_]
mappings = {}
rmappings = {}
for pat in patterns:
if len(pat) == 2:
mappings[pat] = 1
rmappings[1] = pat
elif len(pat) == 4:
mappings[pat] = 4
rmappings[4] = pat
elif len(pat) == 3:
mappings[pat] = 7
rmappings[7] = pat
elif len(pat) == 7:
mappings[pat] = 8
rmappings[8] = pat
# Figure out 2:
# There is one segment that is shared between 1 and 2, and it's the *only*
# segment where that is the case.
shared: DefaultDict[str, List[FrozenSet[str]]] = defaultdict(list)
for pat in patterns:
matches = rmappings[1] & pat
shared[setstr(matches)] += [pat]
# Whichever segment in shared has a length of 1 is seg value 2
for match, pats in shared.items():
if len(pats) == 1:
pat = pats[0]
mappings[pat] = 2
rmappings[2] = pat
break
# pprint.pprint(shared)
# Figure out 9:
for pat in patterns:
matches = rmappings[4] & pat
# If the matching segments are shared with 4's segments, and there
# are 2 more segments, then it's 9
if matches == rmappings[4] and len(pat) == len(rmappings[4]) + 2:
mappings[pat] = 9
rmappings[9] = pat
break
# Figure out 3:
# Subtract the segments of 2 and 1 from the 8 seg, and then subtract
# *that* from the 9 seg
b_seg = (rmappings[8] - rmappings[2]) - rmappings[1]
three = rmappings[9] - b_seg
assert three in patterns
assert three not in mappings
mappings[three] = 3
rmappings[3] = three
# Few more segments
a_seg = rmappings[7] - rmappings[1]
assert len(a_seg) == 1
c_seg = rmappings[2] & rmappings[1]
assert len(c_seg) == 1
e_seg = rmappings[8] - rmappings[9]
assert len(e_seg) == 1
f_seg = rmappings[1] - c_seg
assert len(f_seg) == 1
# Figure out 6:
# Subtract c segment from 8
six = rmappings[8] - c_seg
assert six in patterns
assert six not in mappings
mappings[six] = 6
rmappings[6] = six
# Figure out 5:
# Subtract e and c segments from 8
five = (rmappings[8] - e_seg) - c_seg
assert five in patterns
assert five not in mappings
mappings[five] = 5
rmappings[5] = five
# There's only one number we haven't mapped yet and that's 0, so that's
# the last one.
remaining = frozenset(patterns) - frozenset(mappings.keys())
assert len(remaining) == 1
zero = list(remaining)[0]
mappings[zero] = 0
rmappings[0] = zero
assert len(mappings) == 10
assert len(rmappings) == 10
# Now, figure out each number using our mappings
num = 0
for digit in digits:
num *= 10
num += mappings[frozenset(digit)]
nums += [num]
print(sum(nums))
lines = [
# yeah the line.split[0] and [1] is annoying but it makes it work for a
# comprehension. also it doesn't matter
(line.split(" | ")[0].split(), line.split(" | ")[1].split())
for line in sys.stdin
]
# pprint.pprint(lines)
print("Part 1")
part1(lines)
print("Part 2")
part2(lines)