-
Notifications
You must be signed in to change notification settings - Fork 0
/
day14.py
145 lines (114 loc) · 4.26 KB
/
day14.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
133
134
135
136
137
138
139
140
141
142
143
144
145
"""Solution for Advent of Code day 14."""
from collections import Counter
import doctest
from pathlib import Path
import click
def read_polymer(filename: Path) -> tuple[dict, str]:
"""Read polymer structure information from a file
The polymer is represented by a dict of rules and a polymer structer.
Args:
filename (Path): path to the input file.
Returns:
dict: rules
str: initial polymer structure
Examples:
>>> read_polymer(Path("test_data/day_14.data"))[0]['CH']
'B'
>>> read_polymer(Path("test_data/day_14.data"))[1]
'NNCB'
"""
with filename.open("r") as file:
poly_template, rules_raw = file.read().split("\n\n")
rules = {}
for line in rules_raw.strip().split("\n"):
start, end = line.strip().split(" -> ")
rules[start] = end
return rules, poly_template
def breakdown_structure(connection_counter: Counter, rules: dict) -> Counter:
"""breaksdown the polymer structure and add new elements
Args:
connection_counter (Counter): Initial count of each connection
rules (dict): Rules how to break down each connection
Returns:
Counter: new polymer structure represented by the number for each pairs
Examples:
>>> breakdown_structure(Counter(AB = 1), {"AB":"C"})
Counter({'AC': 1, 'CB': 1})
>>> breakdown_structure(Counter(AB = 1, BC = 4), {"AB":"C", "BC":"A"})
Counter({'AC': 5, 'BA': 4, 'CB': 1})
"""
new_counter = Counter()
for poly_pair in connection_counter:
new_counter[poly_pair[0] + rules[poly_pair]] += connection_counter[poly_pair]
new_counter[rules[poly_pair] + poly_pair[1]] += connection_counter[poly_pair]
return new_counter
def get_puzzel_answer(connection_counter, poly_template) -> int:
"""Count each pair and calculate the puzzel result
What do you get if you take the quantity of the most common element and
subtract the quantity of the least common element?
Args:
connection_counter (Counter): Initial count of each connection
poly_template (str): initial polymer structur
Returns:
int: puzzel result
Examples:
>>> get_puzzel_answer(Counter({'AC': 5, 'BA': 4, 'CB': 1}), "ABC")
3
"""
char_counter = Counter()
for k in connection_counter:
char_counter[k[0]] += connection_counter[k]
# The last charakter of the polymer needs to be counted as well
char_counter[poly_template[-1]] += 1
return max(char_counter.values()) - min(char_counter.values())
@click.group()
def main():
"""CLI for the solution of day 14
Advent of code 2021 (https://adventofcode.com/2021/day/14)
"""
@main.command()
@click.argument(
"filename",
required=False,
type=Path,
default=Path("test_data/day_14.data"),
)
def part_1(filename: Path):
"""Part one of day 14. (10 steps)"""
rules, poly_template = read_polymer(filename)
# Keep track of how often each pair of letters occures in the polymer
connection_counter = Counter()
for i in range(len(poly_template) - 1):
connection_counter[poly_template[i] + poly_template[i + 1]] += 1
for _ in range(10):
connection_counter = breakdown_structure(connection_counter, rules)
print(
"quantity of the most common element - quantity of the least "
f"common element: {get_puzzel_answer(connection_counter,poly_template)}"
)
@main.command()
@click.argument(
"filename",
required=False,
type=Path,
default=Path("test_data/day_14.data"),
)
def part_2(filename: Path):
"""Part two of day 14. (40 steps)"""
rules, poly_template = read_polymer(filename)
# Keep track of how often each pair of letters occures in the polymer
connection_counter = Counter()
for i in range(len(poly_template) - 1):
connection_counter[poly_template[i] + poly_template[i + 1]] += 1
for _ in range(40):
connection_counter = breakdown_structure(connection_counter, rules)
print(
"quantity of the most common element - quantity of the least "
f"common element: {get_puzzel_answer(connection_counter,poly_template)}"
)
@main.command()
def test():
"""run doctest."""
print(doctest.testmod())
if __name__ == "__main__":
main()