summaryrefslogtreecommitdiffstats
path: root/day10/__init__.py
blob: b41ce55b2d4021e009aa92dfd1d26e662279ce9e (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
from collections import Counter
from functools import reduce
from math import floor
from typing import List, Iterator

from aoc import BaseAssignment


class IncompleteLine(Exception):
    def __init__(self, missing: List[str]):
        self.missing = missing

class CorruptedLine(Exception):
    def __init__(self, got: str, expected: str, *args, **kwargs):
        self.excpected = expected
        self.got = got

pairs = {
    '(': ')',
    '[': ']',
    '{': '}',
    '<': '>',
}


class Assignment(BaseAssignment):
    @classmethod
    def parse_line(cls, line: str):
        stack = []
        for character in line:
            if character in pairs:
                stack.append(character)
            elif character == pairs[stack[-1]]:
                stack.pop()
            else:
                raise CorruptedLine(character, pairs[stack[-1]])

        if len(stack) > 0:
            raise IncompleteLine(list(reversed([pairs[c] for c in stack])))


class AssignmentOne(Assignment):
    example_result = 26397

    penalties = {
        ')': 3,
        ']': 57,
        '}': 1197,
        '>': 25137,
    }

    def run(self, input: Iterator[str]) -> int:
        invalid_characters = []
        for line in input:
            try:
                self.parse_line(line)
            except CorruptedLine as c:
                invalid_characters.append(c.got)
            except IncompleteLine:
                continue

        counter = Counter(invalid_characters)

        return sum(counter[k] * v for k, v in self.penalties.items())


class AssignmentTwo(Assignment):
    example_result = 288957

    penalties = {
        ')': 1,
        ']': 2,
        '}': 3,
        '>': 4,
    }

    def run(self, input: Iterator[str]):
        scores = []
        for line in input:
            try:
                self.parse_line(line)
            except CorruptedLine:
                continue
            except IncompleteLine as c:
                scores.append(reduce(
                    lambda out, item: (out * 5) + self.penalties[item],
                    c.missing,
                    0
                ))

        return sorted(scores)[floor(len(scores) / 2)]