-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday-14.py
140 lines (105 loc) · 3.73 KB
/
day-14.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
# --- Day 14: Extended Polymerization ---
# https://adventofcode.com/2021/day/14
from collections import Counter
def parse(input_data):
template, rules = input_data.split("\n\n")
rules = [rule.strip().split(" -> ") for rule in rules.splitlines()]
return template, rules
def count_adjacent_pairs(template):
pairs = []
for i, elem in enumerate(template[1:], start=1):
pairs.append(template[i - 1] + elem)
return Counter(pairs)
def insert(pair_counts, instructions):
new_pairs = Counter()
old_pairs = []
for instruction in instructions:
if instruction[0] in pair_counts:
old_pairs.append(instruction[0])
leading_new_pair = instruction[0][0] + instruction[1]
trailing_new_pair = instruction[1] + instruction[0][1]
new_pairs[leading_new_pair] += pair_counts[instruction[0]]
new_pairs[trailing_new_pair] += pair_counts[instruction[0]]
for pair in old_pairs:
pair_counts[pair] = 0
pair_counts += new_pairs
return pair_counts
def iterate(input_data, n_steps):
template, instructions = parse(input_data)
pair_counts = count_adjacent_pairs(template)
for i in range(n_steps):
pair_counts = insert(pair_counts, instructions)
return pair_counts
def solve(input_data, n):
"""This is not perfect, but it apparently gets with in 1 of the correct answer"""
polymer_pair_counts = iterate(input_data, n)
element_counter = dict()
for k, v in polymer_pair_counts.items():
for elem in list(k):
if elem in element_counter:
element_counter[elem] += v
else:
element_counter[elem] = v
c = Counter(element_counter)
mc = c.most_common()
return round((mc[0][1] - mc[-1][1]) / 2)
# I tried the above approach first, but had trouble getting it to work
# so I implemented the brute force approach. But of course, it doesn't work
# on part 2
def insert_brute_force(template, instructions):
added = 0
to_add = []
for i, s in enumerate(template[1:], start=1):
for (pair, elem) in instructions:
if template[i - 1] + s == pair:
added += 1
to_add.append((i + added - 1, elem))
l = list(template)
for i, elem in to_add:
l.insert(i, elem)
return "".join(l)
def iterate_brute_force(input_data, n_steps):
template, instructions = parse(input_data)
for i in range(n_steps):
template = insert_brute_force(template, instructions)
return template
def solve_brute_force(input_data, n):
polymer = iterate_brute_force(input_data, n)
mc = Counter(polymer).most_common()
return mc[0][1] - mc[-1][1]
if __name__ == "__main__":
from aocd.models import Puzzle
test_data = """NNCB
CH -> B
HH -> N
CB -> H
NH -> C
HB -> C
HC -> B
HN -> C
NN -> C
BH -> H
NC -> B
NB -> B
BN -> B
BB -> N
BC -> B
CC -> N
CN -> C"""
assert iterate_brute_force(test_data, 1) == "NCNBCHB"
assert iterate_brute_force(test_data, 2) == "NBCCNBBBCBHCB"
assert iterate(test_data, 1) == count_adjacent_pairs("NCNBCHB")
assert iterate(test_data, 2) == count_adjacent_pairs("NBCCNBBBCBHCB")
assert iterate(test_data, 3) == count_adjacent_pairs("NBBBCNCCNBBNBNBBCHBHHBCHB")
assert iterate(test_data, 4) == count_adjacent_pairs(
"NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB"
)
assert solve(test_data, 10) == 1588
assert solve_brute_force(test_data, 10) == 1588
puzzle = Puzzle(2021, 14)
answer_1 = solve(puzzle.input_data, 10)
print(answer_1)
puzzle.answer_a = answer_1
answer_2 = solve(puzzle.input_data, 40)
print(answer_2)
puzzle.answer_b = answer_2