-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAoC2021_10.py
132 lines (110 loc) Β· 2.85 KB
/
AoC2021_10.py
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#! /usr/bin/env python3
#
# Advent of Code 2021 Day 10
#
from __future__ import annotations
import sys
from collections import deque
from functools import reduce
from typing import NamedTuple
from aoc.common import InputData
from aoc.common import SolutionBase
from aoc.common import aoc_samples
from aoc.common import log
PAREN_OPEN = "("
PAREN_CLOSE = ")"
SQUARE_OPEN = "["
SQUARE_CLOSE = "]"
CURLY_OPEN = "{"
CURLY_CLOSE = "}"
ANGLE_OPEN = "<"
ANGLE_CLOSE = ">"
OPEN = (PAREN_OPEN, SQUARE_OPEN, CURLY_OPEN, ANGLE_OPEN)
MAP = {
PAREN_OPEN: PAREN_CLOSE,
SQUARE_OPEN: SQUARE_CLOSE,
CURLY_OPEN: CURLY_CLOSE,
ANGLE_OPEN: ANGLE_CLOSE,
PAREN_CLOSE: PAREN_OPEN,
SQUARE_CLOSE: SQUARE_OPEN,
CURLY_CLOSE: CURLY_OPEN,
ANGLE_CLOSE: ANGLE_OPEN,
}
CORRUPTION_SCORES = {
PAREN_CLOSE: 3,
SQUARE_CLOSE: 57,
CURLY_CLOSE: 1_197,
ANGLE_CLOSE: 25_137,
None: 0,
}
INCOMPLETE_SCORES = {
PAREN_OPEN: 1,
SQUARE_OPEN: 2,
CURLY_OPEN: 3,
ANGLE_OPEN: 4,
}
TEST = """\
[({(<(())[]>[[{[]{<()<>>
[(()[<>])]({[<{<<[]>>(
{([(<{}[<>[]}>{[]{[(<()>
(((({<>}<{<{<>}{[]{[]{}
[[<[([]))<([[{}[[()]]]
[{[{({}]{}}([{[{{{}}([]
{<[[]]>}<{[{[{[]{()[[[]
[<(<(<(<{}))><([]([]()
<{([([[(<>()){}]>(<<{{
<{([{{}}[<[[[<>{}]]]>[]]
"""
class Result(NamedTuple):
corrupt: str | None
incomplete: list[str] | None
@classmethod
def for_corrupt(cls, c: str) -> Result:
return Result(c, None)
@classmethod
def for_incomplete(cls, q: list[str]) -> Result:
return Result(None, q)
Input = InputData
Output1 = int
Output2 = int
class Solution(SolutionBase[Input, Output1, Output2]):
def parse_input(self, input_data: InputData) -> Input:
return input_data
def check(self, line: str) -> Result:
stack = deque[str]()
for c in line:
if c in OPEN:
stack.appendleft(c)
elif MAP[c] != stack.popleft():
return Result.for_corrupt(c)
return Result.for_incomplete(list(stack))
def part_1(self, inputs: Input) -> int:
return sum(
CORRUPTION_SCORES[corrupt]
for corrupt in (self.check(line).corrupt for line in inputs)
if corrupt is not None
)
def part_2(self, inputs: Input) -> int:
scores = sorted(
reduce(
lambda a, b: 5 * a + b,
(INCOMPLETE_SCORES[i] for i in incomplete),
)
for incomplete in (self.check(line).incomplete for line in inputs)
if incomplete is not None
)
log(scores)
return scores[len(scores) // 2]
@aoc_samples(
(
("part_1", TEST, 26_397),
("part_2", TEST, 288_957),
)
)
def samples(self) -> None:
pass
solution = Solution(2021, 10)
def main() -> None:
solution.run(sys.argv)
if __name__ == "__main__":
main()