summaryrefslogtreecommitdiffstats
path: root/day8/__init__.py
blob: 4a72c1f51ca15b939f8065a56d99c882a80d5043 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
from collections import Counter
from copy import copy
from typing import List, Tuple, Dict, FrozenSet

from aoc import BaseAssignment

class UnknownException(Exception):
    pass

Item = FrozenSet[str]
Information = Tuple[List[Item], List[Item]]

class Assignment(BaseAssignment):
    def parse_item(self, item: str) -> Tuple[List[Item], List[Item]]:
        input, output = item.split('|')

        return (
            [frozenset(i.strip()) for i in input.split(' ') if i != ''],
            [frozenset(i.strip()) for i in output.split(' ') if i != '']
        )

    def read_input(self, example = False) -> List[Tuple[List[str], List[str]]]:
        return list(super().read_input(example))

    @staticmethod
    def calc_digit(item: Item, known_numbers: Dict[int, Item] = None) -> int:
        if known_numbers is None:
            known_numbers = {}

        match len(item):
            case 2:
                return 1
            case 3:
                return 7
            case 4:
                return 4
            case 5:
                if 1 in known_numbers and item.issuperset(known_numbers[1]):
                    return 3
                elif 4 in known_numbers and len(item | known_numbers[4]) == 7:
                    return 2
                else:
                    return 5
            case 6:
                if 1 in known_numbers and not item.issuperset(known_numbers[1]):
                    return 6
                elif 3 in known_numbers and item.issuperset(known_numbers[3]):
                    return 9
                else:
                    return 0
            case 7:
                return 8
            case _:
                raise UnknownException()

    @staticmethod
    def unique_filter(item: Item) -> bool:
        try:
            return Assignment.calc_digit(item) in [1, 4, 7, 8]
        except UnknownException:
            return False


class AssignmentOne(Assignment):
    example_result = 26

    def run(self, input: List[Information]) -> int:
        return sum([
            len(list(filter(Assignment.unique_filter, output)))
            for _, output in input
        ])


class AssignmentTwo(Assignment):
    example_result = 61229

    @staticmethod
    def find_all_numbers(items: List[Item]) -> Dict[int, Item]:
        known_numbers = {
            Assignment.calc_digit(item): item
            for item in filter(Assignment.unique_filter, items)
        }

        unknown_numbers = {
            item
            for item
            in filter(
                lambda item: item not in known_numbers.values(),
                items
            )
        }

        while len(unknown_numbers) > 0:
            for number in copy(unknown_numbers):
                try:
                    nr = Assignment.calc_digit(number, known_numbers)
                    known_numbers[nr] = number
                    unknown_numbers.remove(number)
                except UnknownException:
                    continue

        return known_numbers

    def run(self, input: List[Information]) -> int:
        return sum([
            sum(
                self.calc_digit(item, self.find_all_numbers(input + output))
                    * (10 ** (len(output) - index - 1))
                for index, item in enumerate(output)
            )
            for input, output in input
        ])