diff --git a/2021/08/README.md b/2021/08/README.md new file mode 100644 index 0000000..d17ef1c --- /dev/null +++ b/2021/08/README.md @@ -0,0 +1,27 @@ +# 08 + +This was super fun and another episode of "I should have gotten my paper out sooner". + +As soon as I realized you can take the statistical approach to decode the numbers. + +1. Count all characters + +This will reveal the mapping for the letters `b`, `e` and `f`, as they appear a unique amount of times. + +2. `a` & `c` + +Both appear 8 times. We find `a` by looking at the `1` and `7`. If we identify `1` (cf) and `7` (afc) we can simply calculate what `a` is by difference. With `a` identified `c` is also determined. + +3. `d` & `g` + +Both appear 7 times. We take the similar approach as in 2. + +This ensures a linear time execution. + +
+ Solutions +
    +
  1. 449
  2. +
  3. 968175
  4. +
+
diff --git a/2021/08/python/main.py b/2021/08/python/main.py new file mode 100644 index 0000000..c9efa84 --- /dev/null +++ b/2021/08/python/main.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python + +from os.path import join, dirname +from typing import Dict, List, Set + +# Day 08 + +# Common + + +def read_input(filename): + data = join(dirname(__file__), '..', filename) + with open(data) as f: + return f.read().strip() + + +test = read_input('test.txt') +data = read_input('input.txt') + + +class Utils: + @staticmethod + def count_chars(items: List[str]) -> Dict[str, int]: + count = {} + for item in items: + for char in item: + if char not in count: + count[char] = 0 + count[char] += 1 + return count + + @staticmethod + def group_by_length(items: List[str]) -> Dict[int, List[str]]: + groups = {} + for item in items: + l = len(item) + if l not in groups: + groups[l] = [] + groups[l].append(item) + return groups + + +class SevenSegment: + mappings = { + 0: 'abcefg', + 1: 'cf', + 2: 'acdeg', + 3: 'acdfg', + 4: 'bcdf', + 5: 'abdfg', + 6: 'abdefg', + 7: 'acf', + 8: 'abcdefg', + 9: 'abcdfg' + } + reverse = {v: k for k, v in mappings.items()} + count: Dict[str, int] = Utils.count_chars(mappings.values()) + + @staticmethod + def encode(number: int) -> str: + return SevenSegment.mappings[number] + + @staticmethod + def decode(segment: str) -> int: + normalized: str = ''.join(sorted(list(segment))) + return SevenSegment.reverse[normalized] + + +class Reading: + def __init__(self, input: List[str], output: List[str]) -> None: + self.input = input + self.output = output + self.transformations: Dict[str, str] = {} + + def translate(self, segment: str) -> str: + return ''.join([self.transformations[char] for char in segment]) + + def analyse(self): + count = Utils.count_chars(self.input) + by_length = Utils.group_by_length(self.input) + + # Find a & c + # 1 and 7 have the same chars with the only difference of the 'a' + one = set(by_length[2][0]) + seven = set(by_length[3][0]) + a = seven.difference(one).pop() + + dg: Set[str] = set() + + # Find b, e & f by statistical analysis + for k, v in count.items(): + if v == 6: + b = k + self.transformations[k] = 'b' + elif v == 4: + self.transformations[k] = 'e' + elif v == 9: + self.transformations[k] = 'f' + elif v == 8: + if k == a: + self.transformations[k] = 'a' + else: + self.transformations[k] = 'c' + elif v == 7: + dg.add(k) + + # Find d by looking at the 1 and 4 + # We already know b so, we subtract cf (1) from bcdf (4) and are left with bd. + four = set(by_length[4][0]) + bd = four.difference(one) + d = bd.difference(b).pop() + self.transformations[d] = 'd' + g = dg.difference(d).pop() + self.transformations[g] = 'g' + + def read(self) -> List[int]: + return [SevenSegment.decode(self.translate(segment)) for segment in self.output] + + @staticmethod + def parse(data: str): + input, output = data.split('|') + i = input.strip().split(' ') + o = output.strip().split(' ') + return Reading(i, o) + + +class Log: + def __init__(self, readings: List[Reading]) -> None: + self.readings = readings + + def flag(self, alt=False): + count = 0 + for reading in self.readings: + reading.analyse() + numbers = reading.read() + if alt: + count += int(''.join([str(n) for n in numbers])) + else: + for number in numbers: + if number == 1 or number == 4 or number == 7 or number == 8: + count += 1 + return count + + @staticmethod + def parse(data: str): + return Log([Reading.parse(line) for line in data.split('\n')]) + + +# 1 +print('1.') + +log = Log.parse(test) +result = log.flag() +print(f"Test: {result}") + +log = Log.parse(data) +result = log.flag() +print(f"Result: {result}") + +# 2 +print('\n2.') + +log = Log.parse(test) +result = log.flag(True) +print(f"Test: {result}") + +log = Log.parse(data) +result = log.flag(True) +print(f"Result: {result}")