2021 Day 14

This commit is contained in:
Akumatic 2021-12-14 12:16:02 +01:00
parent e2466aa44a
commit 5929e63062
7 changed files with 274 additions and 1 deletions

68
2021/14/README.md Normal file
View File

@ -0,0 +1,68 @@
# 2021 Day 14: Extended Polymerization
Copyright (c) Eric Wastl
#### [Direct Link](https://adventofcode.com/2021/day/14)
## Part 1
The incredible pressures at this depth are starting to put a strain on your submarine. The submarine has [polymerization](https://en.wikipedia.org/wiki/Polymerization) equipment that would produce suitable materials to reinforce the submarine, and the nearby volcanically-active caves should even have the necessary input elements in sufficient quantities.
The submarine manual contains instructions for finding the optimal polymer formula; specifically, it offers a **polymer template** and a list of **pair insertion** rules (your puzzle input). You just need to work out what polymer would result after repeating the pair insertion process a few times.
For example:
```
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
```
The first line is the **polymer template** - this is the starting point of the process.
The following section defines the **pair insertion** rules. A rule like `AB -> C` means that when elements `A` and `B` are immediately adjacent, element `C` should be inserted between them. These insertions all happen simultaneously.
So, starting with the polymer template `NNCB`, the first step simultaneously considers all three pairs:
- The first pair (`NN`) matches the rule `NN` -> `C`, so element `C` is inserted between the first `N` and the second `N`.
- The second pair (`NC`) matches the rule `NC` -> `B`, so element `B` is inserted between the `N` and the `C`.
- The third pair (`CB`) matches the rule `CB -> H`, so element `H is inserted between the `C` and the `B`.
Note that these pairs overlap: the second element of one pair is the first element of the next pair. Also, because all pairs are considered simultaneously, inserted elements are not considered to be part of a pair until the next step.
After the first step of this process, the polymer becomes `NCNBCHB`.
Here are the results of a few steps using the above rules:
```
Template: NNCB
After step 1: NCNBCHB
After step 2: NBCCNBBBCBHCB
After step 3: NBBBCNCCNBBNBNBBCHBHHBCHB
After step 4: NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB
```
This polymer grows quickly. After step 5, it has length 97; After step 10, it has length 3073. After step 10, `B` occurs 1749 times, `C` occurs 298 times, `H` occurs 161 times, and `N` occurs 865 times; taking the quantity of the most common element (B, 1749) and subtracting the quantity of the least common element (`H`, 161) produces `1749 - 161 = 1588`.
Apply 10 steps of pair insertion to the polymer template and find the most and least common elements in the result. **What do you get if you take the quantity of the most common element and subtract the quantity of the least common element?**
## Part 2
The resulting polymer isn't nearly strong enough to reinforce the submarine. You'll need to run more steps of the pair insertion process; a total of **40 steps** should do it.
In the above example, the most common element is `B` (occurring `2192039569602` times) and the least common element is `H` (occurring `3849876073` times); subtracting these produces **`2188189693529`**.
Apply **40** steps of pair insertion to the polymer template and find the most and least common elements in the result. **What do you get if you take the quantity of the most common element and subtract the quantity of the least common element?**

48
2021/14/code.py Normal file
View File

@ -0,0 +1,48 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 Akumatic
#
# https://adventofcode.com/2021/day/14
def read_file(filename: str = "input.txt") -> tuple:
with open(f"{__file__.rstrip('code.py')}{filename}", "r") as f:
lines = [line for line in f.read().strip().split("\n")]
template = lines[0]
rules = {x[0]: x[1] for x in (line.split(" -> ") for line in lines[2:])}
return template, rules
def add_to_dict(d: dict, key: str, val: int = 1):
if key not in d:
d[key] = val
else:
d[key] += val
def count(polymer: str, rules: dict, iterations: int) -> dict:
pairs = dict()
for i in range(len(polymer) - 1):
add_to_dict(pairs, polymer[i] + polymer[i+1])
for _ in range(iterations):
tmp = dict()
for pair in pairs:
add_to_dict(tmp, pair[0] + rules[pair], pairs[pair])
add_to_dict(tmp, rules[pair] + pair[1], pairs[pair])
pairs = tmp
elements = dict()
for pair in pairs:
add_to_dict(elements, pair[0], pairs[pair])
add_to_dict(elements, polymer[-1])
return elements
def part1(template: str, insertion_rules: dict) -> int:
letters = count(template, insertion_rules, 10)
return max(letters.values()) - min(letters.values())
def part2(template: str, insertion_rules: dict) -> int:
letters = count(template, insertion_rules, 40)
return max(letters.values()) - min(letters.values())
if __name__ == "__main__":
vals = read_file() # vals[0] is the template, vals[1] the insertion rules
print(f"Part 1: {part1(*vals)}")
print(f"Part 2: {part2(*vals)}")

102
2021/14/input.txt Normal file
View File

@ -0,0 +1,102 @@
HBCHSNFFVOBNOFHFOBNO
HF -> O
KF -> F
NK -> F
BN -> O
OH -> H
VC -> F
PK -> B
SO -> B
PP -> H
KO -> F
VN -> S
OS -> B
NP -> C
OV -> C
CS -> P
BH -> P
SS -> P
BB -> H
PH -> V
HN -> F
KV -> H
HC -> B
BC -> P
CK -> P
PS -> O
SH -> N
FH -> N
NN -> P
HS -> O
CB -> F
HH -> F
SB -> P
NB -> F
BO -> V
PN -> H
VP -> B
SC -> C
HB -> H
FP -> O
FC -> H
KP -> B
FB -> B
VK -> F
CV -> P
VF -> V
SP -> K
CC -> K
HV -> P
NC -> N
VH -> K
PF -> P
PB -> S
BF -> K
FF -> C
FV -> V
KS -> H
VB -> F
SV -> F
HO -> B
FN -> C
SN -> F
OB -> N
KN -> P
BV -> H
ON -> N
NF -> S
OF -> P
NV -> S
VS -> C
OO -> C
BP -> H
BK -> N
CP -> N
PC -> K
CN -> H
KB -> B
BS -> P
KK -> P
SF -> V
CO -> V
CH -> P
FO -> B
FS -> F
VO -> H
NS -> F
KC -> H
VV -> K
NO -> P
OK -> F
PO -> V
FK -> H
OP -> H
PV -> N
CF -> P
NH -> K
SK -> O
KH -> P
HP -> V
OC -> V
HK -> F

2
2021/14/solution.txt Normal file
View File

@ -0,0 +1,2 @@
Part 1: 3408
Part 2: 3724343376942

35
2021/14/test_code.py Normal file
View File

@ -0,0 +1,35 @@
# SPDX-License-Identifier: MIT
# Copyright (c) 2021 Akumatic
from code import part1, part2, read_file, count
def count_chars(s: str) -> dict:
cnt = dict()
for c in s:
if c in cnt:
cnt[c] += 1
else:
cnt[c] = 1
return cnt
def test():
vals = read_file("test_input.txt")
assert count(vals[0], vals[1], 1) == count_chars("NCNBCHB")
assert count(vals[0], vals[1], 2) == count_chars("NBCCNBBBCBHCB")
assert count(vals[0], vals[1], 3) == count_chars("NBBBCNCCNBBNBNBBCHBHHBCHB")
assert count(vals[0], vals[1], 4) == count_chars("NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB")
print("Passed Counting")
elements = count(vals[0], vals[1], 10)
assert elements == {"B": 1749, "C": 298, "H": 161, "N": 865}
assert part1(*vals) == 1588
print("Passed Part 1")
elements = count(vals[0], vals[1], 40)
assert elements["B"] == 2192039569602
assert elements["H"] == 3849876073
assert part2(*vals) == 2188189693529
print("Passed Part 2")
if __name__ == "__main__":
test()

18
2021/14/test_input.txt Normal file
View File

@ -0,0 +1,18 @@
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

View File

@ -28,7 +28,7 @@ Collect stars by solving puzzles. Two puzzles will be made available on each day
| 11 | :white_check_mark: | :white_check_mark: | [Solution](11/code.py) | [Day 11](https://adventofcode.com/2021/day/11) |
| 12 | :white_check_mark: | :white_check_mark: | [Solution](12/code.py) | [Day 12](https://adventofcode.com/2021/day/12) |
| 13 | :white_check_mark: | :white_check_mark: | [Solution](13/code.py) | [Day 13](https://adventofcode.com/2021/day/13) |
| 14 | | | | |
| 14 | :white_check_mark: | :white_check_mark: | [Solution](14/code.py) | [Day 14](https://adventofcode.com/2021/day/14) |
| 15 | | | | |
| 16 | | | | |
| 17 | | | | |