from misc.helper import verify_answer
= "../inputs/example_day_19.txt"
example_input_file = "../inputs/day_19.txt" input_file
Day 19
For the Day 19 of our little adventure we land in the hot springs on Gear Island! This time nothing is catastrophic, except for the fact we are short on cash to explore the onsen. To get the money we agree to figure out if the towels can be arranged into specified patterns!
Read the full story on the official Day 19 of Advent of Code 2024 website.
And the usual ChatGPT generated illustration (tho I must admit it is hard to get it to generate what I want, I might try ChatGPT 4 again, cause 4o seems to be less good at images and, unrelated, emails) to get us started:
Part 1: Combine patterns
Our task here is from a given set of towels, count how many of the patterns can be assembled.
Towels can have stipes that are white (w), blue (u), black (b), red (r), or green (g).
Let’s say we have towels with the following patterns: wub
, ggb
, rrggbb
, rr
, b
.
And we want to see if the following patterns can be assembled: ggbrrb
, wurggb
, rrrrrr
, rrggbbrr
, brr
and rgb
.
ggbrrb
can be assembled fromggb
andrr
andb
,wurggb
cannot be assembled,rrrrrr
can be assembled fromrr
andrr
andrr
,rrggbbrr
can be assembled fromrr
andggb
andb
andrr
orrrggbb
andrr
,brr
can be assembled fromb
andrr
,rgb
cannot be assembled.
The number of patterns that can be assembled from the given set of towels is 4
.
from pathlib import Path
= """
example wub, ggb, rrggbb, rr, b
ggbrrb
wurggb
rrrrrr
rrggbbrr
brr
rgb
"""
def parse_input(input):
if Path(input).exists():
with open(input) as f:
input = f.read()
= []
patterns = []
towels for line in input.strip().split("\n"):
if "," in line:
= line.split(", ")
towels elif line == "":
continue
else:
patterns.append(line)return towels, patterns
parse_input(example)
(['wub', 'ggb', 'rrggbb', 'rr', 'b'],
['ggbrrb', 'wurggb', 'rrrrrr', 'rrggbbrr', 'brr', 'rgb'])
def is_pattern_possible(towels, pattern):
if pattern == "":
return True
= [towel for towel in towels if pattern.startswith(towel)]
possible_towels = [pattern[len(towel):] for towel in possible_towels]
matching_patterns
if matching_patterns:
return any(is_pattern_possible(towels, pattern) for pattern in matching_patterns)
else:
return False
def part_one(input):
= parse_input(input)
towels, patterns return sum(is_pattern_possible(towels, pattern) for pattern in patterns)
4) verify_answer(part_one, example,
✔️ That's right! The answer is 4.
6) verify_answer(part_one, example_input_file,
✔️ That's right! The answer is 6.
%time part_one(input_file)
CPU times: user 50.8 ms, sys: 2.03 ms, total: 52.9 ms
Wall time: 58.8 ms
296
That’s the right answer! You are one gold star ⭐ closer to finding the Chief Historian.
Part 2: All the patterns!
The staff is not in love with just one option to arrange the towels into patterns. They want to know all the possible patterns that can be assembled from the given set of towels.
Since this time we need to count all the possible patterns, we need to be more careful about the
def all_possible_patterns(towels, pattern, cache=None):
# create a cache to store the results of already calculated patterns
# this is created here instead of cache={} in the function signature
# to avoid mutable default arguments
if cache is None:
= {}
cache
# if we have already calculated the result for this pattern, return it
if pattern in cache:
return cache[pattern]
# if the pattern is empty, we have found a valid pattern
if pattern == "":
return 1
= [towel for towel in towels if pattern.startswith(towel)]
possible_towels = [pattern[len(towel) :] for towel in possible_towels]
matching_patterns
if matching_patterns:
= sum(
result
all_possible_patterns(towels, subpattern, cache)for subpattern in matching_patterns
)else:
= 0
result
# cache the result to only test subpatterns once
= result
cache[pattern]
return result
def part_two(input):
= parse_input(input)
towels, patterns return sum(all_possible_patterns(towels, pattern) for pattern in patterns)
5) verify_answer(part_two, example,
✔️ That's right! The answer is 5.
16) verify_answer(part_two, example_input_file,
✔️ That's right! The answer is 16.
%time part_two(input_file)
CPU times: user 112 ms, sys: 3.69 ms, total: 116 ms
Wall time: 123 ms
619970556776002
That’s the right answer! You are one gold star ⭐ closer to finding the Chief Historian.