-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Sara Veldhoen
committed
Dec 16, 2021
1 parent
38150b0
commit 3c1af9a
Showing
4 changed files
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
import pathlib | ||
import aocd | ||
from collections import Counter | ||
import sys | ||
|
||
|
||
def parse(puzzle_input): | ||
lines = puzzle_input.splitlines() | ||
template = lines[0] | ||
rules = {line.split(' -> ')[0]:line.split(' -> ')[1] for line in lines[2:]} | ||
return template, rules | ||
"""Parse input""" | ||
|
||
def compute_score_counter(counter): | ||
c = counter.most_common() | ||
return c[0][1] - c[-1][1] | ||
|
||
def apply_steps_for_pairs(template,cat_rules, n): | ||
cnt = Counter(template) | ||
polymer = '' | ||
for i in range(len(template) - 1): | ||
polymer += template[i] | ||
c, s = apply_steps_pair(template[i:i+2],cat_rules, n) | ||
cnt += c | ||
polymer += s | ||
polymer += template[-1] | ||
return cnt, polymer | ||
|
||
|
||
def expand(parents, rules, n): | ||
# we know that every right branch will always expend the same, so focus on the left expansions only | ||
child = rules[parents] | ||
if n>1: | ||
left = parents[0]+child | ||
if left == parents: | ||
True# don't go into this again, we're already working this out for one level higher | ||
else: | ||
expand(left, rules, n-1) | ||
right = child+parents[1] | ||
if right == parents: | ||
True | ||
# don't go into this again, we're already working this out for one level higher | ||
else: | ||
expand(right,rules,n-1) | ||
|
||
|
||
|
||
|
||
def apply_steps_pair(parents, cat_rules, n): | ||
polymer = '' | ||
case = 'left' if parents in cat_rules['left'] else 'right' if parents in cat_rules['right'] else 'other' | ||
#breakpoint() | ||
if parents in cat_rules['left']: | ||
child = cat_rules['left'][parents] | ||
descendants = Counter({child:n}) | ||
polymer+= n*child | ||
if n>1: | ||
c, s = apply_steps_pair(child+parents[1], cat_rules,n-1) | ||
descendants += c | ||
polymer += s | ||
elif parents in cat_rules['right']: | ||
child = cat_rules['right'][parents] | ||
descendants = Counter({child: n}) | ||
if n>1: | ||
c, s = apply_steps_pair(parents[0]+child,cat_rules,n-1) | ||
descendants +=c | ||
polymer += s | ||
polymer += n * child | ||
else: | ||
child = cat_rules['other'][parents] | ||
descendants = Counter({child:1}) | ||
if n>1: | ||
c, s = apply_steps_pair(parents[0] + child, cat_rules, n - 1) | ||
descendants += c | ||
polymer += s | ||
polymer += child | ||
c, s = apply_steps_pair(child + parents[1], cat_rules, n - 1) | ||
descendants += c | ||
polymer += s | ||
else: | ||
polymer += child | ||
return descendants, polymer | ||
|
||
|
||
def null_expansions(rules): | ||
return {0:{key: key[0]+value+key[1] for key, value in rules.items}} | ||
|
||
|
||
|
||
|
||
def apply_step(template, rules): | ||
polymer = '' | ||
for i in range(len(template)-1): | ||
polymer+=template[i] + rules[template[i:i+2]] | ||
polymer += template[i+1] | ||
return polymer | ||
|
||
def apply_steps(polymer, rules, n): | ||
for i in range(n): | ||
polymer = apply_step(polymer,rules) | ||
return polymer | ||
|
||
|
||
def pair_counts(pair, rules, known_counts, depth): | ||
if depth not in known_counts: | ||
known_counts[depth]={} | ||
if depth-1 not in known_counts: | ||
known_counts[depth-1] = {} | ||
if depth > 0: | ||
child = rules[pair] | ||
left = pair[0]+child | ||
if left not in known_counts[depth-1]: | ||
known_counts = pair_counts(left, rules, known_counts, depth-1) | ||
assert left in known_counts[depth-1] | ||
right = child+pair[1] | ||
if right not in known_counts[depth - 1]: | ||
known_counts = pair_counts(right, rules, known_counts, depth-1) | ||
assert right in known_counts[depth - 1] | ||
known_counts[depth][pair] = known_counts[depth-1][left]+known_counts[depth-1][right] | ||
known_counts[depth][pair][child] -= 1 # ugly way to make sure child is only counted once | ||
else: | ||
if pair not in known_counts[depth]: | ||
known_counts[depth][pair] = Counter(pair) | ||
return known_counts | ||
|
||
def all_pair_counts(rules, to_depth): | ||
counts = {0:{pair:Counter(pair) for pair in rules.keys()}} | ||
for pair in rules.keys(): | ||
counts = pair_counts(pair,rules,counts,to_depth) | ||
return counts | ||
|
||
def count_based_on_counts(template, rules, depth): | ||
counts = all_pair_counts(rules, depth) | ||
score = Counter() | ||
for i in range(len(template) - 1): | ||
score += counts[depth][template[i:i + 2]] | ||
score[template[i + 1]] -= 1 | ||
score[template[-1]] += 1 | ||
return score | ||
|
||
|
||
def compute_score_polymer(polymer): | ||
c = Counter(polymer).most_common() | ||
return c[0][1] - c[-1][1] | ||
|
||
def part1(data): | ||
template, rules = parse(data) | ||
#return compute_score_polymer(apply_steps(template,rules,10)) | ||
#return compute_score_counter(apply_steps_for_pairs(template, rules, 10)) | ||
return compute_score_counter(count_based_on_counts(template, rules, 10)) | ||
|
||
"""Solve part 1""" | ||
|
||
|
||
|
||
def part2(data): | ||
template, rules = parse(data) | ||
|
||
return compute_score_counter(count_based_on_counts(template, rules, 40)) | ||
|
||
|
||
"""Solve part 2""" | ||
|
||
def solve(day=14): | ||
|
||
"""Solve the puzzle for the given input""" | ||
|
||
data = aocd.get_data(day=day) | ||
|
||
print('Part one:', part1(data)) | ||
print('Part two:', part2(data)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import pathlib | ||
import aocd | ||
|
||
import sys | ||
|
||
|
||
def parse(puzzle_input): | ||
|
||
"""Parse input""" | ||
|
||
|
||
def part1(data): | ||
|
||
"""Solve part 1""" | ||
|
||
|
||
def part2(data): | ||
|
||
"""Solve part 2""" | ||
|
||
|
||
def solve(day=-1): | ||
|
||
"""Solve the puzzle for the given input""" | ||
|
||
data = aocd.get_data(day=day) | ||
|
||
print('Part one:', part1(data)) | ||
print('Part two:', part2(data)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import collections | ||
|
||
import aoc_14 as target | ||
|
||
|
||
def test_parse(): | ||
template, rules = target.parse(example_data) | ||
assert template == example_template | ||
assert rules == example_rules | ||
|
||
|
||
def test_apply_step(): | ||
step_1 = target.apply_step(example_template, example_rules) | ||
assert step_1 == example_step_1 | ||
step_2 = target.apply_step(step_1, example_rules) | ||
assert step_2 == example_step_2 | ||
step_3 = target.apply_step(step_2, example_rules) | ||
assert step_3 == example_step_3 | ||
step_4 = target.apply_step(step_3, example_rules) | ||
assert step_4 == example_step_4 | ||
|
||
|
||
def test_apply_steps(): | ||
step_4 = target.apply_steps(example_template, example_rules, 4) | ||
assert step_4 == example_step_4 | ||
|
||
|
||
def test_compute_score(): | ||
step_10 = target.apply_steps(example_template, example_rules, 10) | ||
assert target.compute_score(step_10) == 1588 | ||
|
||
|
||
def test_part1(): | ||
assert False | ||
|
||
|
||
def test_part2(): | ||
assert False | ||
|
||
|
||
example_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""" | ||
|
||
example_rules = {'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'} | ||
|
||
example_template = """NNCB""" | ||
example_step_1 = "NCNBCHB" | ||
example_step_2 = "NBCCNBBBCBHCB" | ||
example_step_3 = "NBBBCNCCNBBNBNBBCHBHHBCHB" | ||
example_step_4 = "NBBNBNBBCCNBCNCCNBBNBBNBBBNBBNBBCBHCBHHNHCBBCBHCB" | ||
|
||
example_counter_10 = collections.Counter({'B': 1749, 'C': 298, 'H': 161, 'N': 865}) | ||
|
||
|
||
def test_compute_score_counter(): | ||
assert target.compute_score_counter(example_counter_10) == 1588 | ||
|
||
|
||
def test_apply_steps_for_pairs(): | ||
cat_rules = target.categorize_rules(example_rules) | ||
example_steps = [example_step_1, example_step_2, example_step_3, example_step_4] | ||
# for i, example in enumerate(example_steps): | ||
# c, s = target.apply_steps_for_pairs(example_template, cat_rules, i+1) | ||
# assert s == example | ||
# assert c == collections.Counter(example) | ||
|
||
c, s = target.apply_steps_for_pairs(example_steps[0], cat_rules, 3) | ||
assert s == example_steps[3] | ||
assert c == collections.Counter(example_steps[3]) | ||
|
||
|
||
def test_apply_steps_pair(): | ||
cat_rules = target.categorize_rules(example_rules) | ||
parents = 'CN' | ||
depth, expansion = 3, 'CCNBCNCCN' | ||
# depth, expansion = 2, 'CNCCN' | ||
|
||
# parents = 'CC' | ||
# depth, expansion = 1, 'CNC' | ||
c, s = target.apply_steps_pair(parents, cat_rules, depth) | ||
assert parents[0] + s + parents[1] == expansion | ||
assert c + collections.Counter(parents) == collections.Counter(expansion) | ||
|
||
|
||
def test_all_pair_counts(): | ||
example_steps = [example_template,example_step_1, example_step_2, example_step_3, example_step_4] | ||
for depth, example in enumerate(example_steps): | ||
counts = target.all_pair_counts(example_rules, depth) | ||
score = collections.Counter() | ||
for i in range(len(example_template)-1): | ||
score += counts[depth][example_template[i:i+2]] | ||
score[example_template[i+1]]-=1 | ||
score[example_template[-1]]+=1 | ||
assert score==collections.Counter(example) | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
def test_read_packet(): | ||
assert False |