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 arrange the towels in the onsen!
+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 57 ms, sys: 3.59 ms, total: 60.6 ms
+Wall time: 92.3 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 110 ms, sys: 3.95 ms, total: 114 ms
+Wall time: 150 ms
+619970556776002
+++That’s the right answer! You are one gold star ⭐ closer to finding the Chief Historian.
+