diff --git a/2024/day_19/chatgpt_image.webp b/2024/day_19/chatgpt_image.webp new file mode 100644 index 0000000..1517a64 Binary files /dev/null and b/2024/day_19/chatgpt_image.webp differ diff --git a/2024/day_19/index.html b/2024/day_19/index.html new file mode 100644 index 0000000..62d9bb7 --- /dev/null +++ b/2024/day_19/index.html @@ -0,0 +1,3772 @@ + + + + + + + + + + + +Day 19 + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+
+

Day 19

+
+ + + +
+ +
+
Author
+
+

Kasia Kedzierska

+
+
+ +
+
Published
+
+

December 19, 2024

+
+
+ +
+
Modified
+
+

December 19, 2024

+
+
+ +
+ + + +
+ + +

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:

+
+
+

+
+
+
+ +
+
+
+
from misc.helper import verify_answer
+
+example_input_file = "../inputs/example_day_19.txt"
+input_file = "../inputs/day_19.txt"
+
+
+
+
+
+

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 from ggb and rr and b,
  • +
  • wurggb cannot be assembled,
  • +
  • rrrrrr can be assembled from rr and rr and rr,
  • +
  • rrggbbrr can be assembled from rr and ggb and b and rr or rrggbb and rr,
  • +
  • brr can be assembled from b and rr,
  • +
  • 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:
+            towels = line.split(", ")
+        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
+    
+    possible_towels = [towel for towel in towels if pattern.startswith(towel)]
+    matching_patterns = [pattern[len(towel):] for towel in possible_towels]
+
+    if matching_patterns:
+        return any(is_pattern_possible(towels, pattern) for pattern in matching_patterns)
+    else:
+        return False
+
+def part_one(input):
+    towels, patterns = parse_input(input)
+    return sum(is_pattern_possible(towels, pattern) for pattern in patterns)
+
+
+
verify_answer(part_one, example, 4)
+
+
✔️ That's right! The answer is 4.
+
+
+
+
verify_answer(part_one, example_input_file, 6)
+
+
✔️ 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  
+
+    possible_towels = [towel for towel in towels if pattern.startswith(towel)]
+    matching_patterns = [pattern[len(towel) :] for towel in possible_towels]
+
+    if matching_patterns:
+        result = sum(
+            all_possible_patterns(towels, subpattern, cache)
+            for subpattern in matching_patterns
+        )
+    else:
+        result = 0
+
+    # cache the result to only test subpatterns once
+    cache[pattern] = result
+
+    return result
+
+
+def part_two(input):
+    towels, patterns = parse_input(input)
+    return sum(all_possible_patterns(towels, pattern) for pattern in patterns)
+
+
+
verify_answer(part_two, example, 5)
+
+
✔️ That's right! The answer is 5.
+
+
+
+
verify_answer(part_two, example_input_file, 16)
+
+
✔️ 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.

+
+
+ +
+ + +
+ + + + + \ No newline at end of file diff --git a/2024/day_19/misc b/2024/day_19/misc new file mode 120000 index 0000000..639ee29 --- /dev/null +++ b/2024/day_19/misc @@ -0,0 +1 @@ +../misc \ No newline at end of file diff --git a/2024/day_19/notebook.ipynb b/2024/day_19/notebook.ipynb new file mode 100644 index 0000000..0f0692b --- /dev/null +++ b/2024/day_19/notebook.ipynb @@ -0,0 +1,376 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "---\n", + "title: 'Day 19'\n", + "author: \"Kasia Kedzierska\"\n", + "date: December 19, 2024\n", + "date-modified: last-modified\n", + "format:\n", + " html:\n", + " theme: sketchy\n", + " css: misc/custom.css\n", + " code-fold: false\n", + " embed-resources: true\n", + " toc: true\n", + "jupyter: python3\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "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! \n", + "\n", + "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:\n", + "\n", + "![](chatgpt_image.webp){width=75% fig-align=\"center\"}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "::: {.callout-note collapse=\"true\" title=\"Setting up\"}" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from misc.helper import verify_answer\n", + "\n", + "example_input_file = \"../inputs/example_day_19.txt\"\n", + "input_file = \"../inputs/day_19.txt\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 1: Combine patterns\n", + "\n", + "Our task here is from a given set of towels, count how many of the patterns can be assembled. \n", + "\n", + "Towels can have stipes that are white (w), blue (u), black (b), red (r), or green (g).\n", + "\n", + "Let's say we have towels with the following patterns: `wub`, `ggb`, `rrggbb`, `rr`, `b`. \n", + "\n", + "And we want to see if the following patterns can be assembled: `ggbrrb`, `wurggb`, `rrrrrr`, `rrggbbrr`, `brr` and `rgb`.\n", + "\n", + "- `ggbrrb` can be assembled from `ggb` and `rr` and `b`, \n", + "- `wurggb` cannot be assembled,\n", + "- `rrrrrr` can be assembled from `rr` and `rr` and `rr`,\n", + "- `rrggbbrr` can be assembled from `rr` and `ggb` and `b` and `rr` or `rrggbb` and `rr`,\n", + "- `brr` can be assembled from `b` and `rr`,\n", + "- `rgb` cannot be assembled.\n", + "\n", + "The number of patterns that can be assembled from the given set of towels is `4`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(['wub', 'ggb', 'rrggbb', 'rr', 'b'],\n", + " ['ggbrrb', 'wurggb', 'rrrrrr', 'rrggbbrr', 'brr', 'rgb'])" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pathlib import Path\n", + "\n", + "example = \"\"\"\n", + "wub, ggb, rrggbb, rr, b\n", + "\n", + "ggbrrb\n", + "wurggb\n", + "rrrrrr\n", + "rrggbbrr\n", + "brr\n", + "rgb\n", + "\"\"\"\n", + "\n", + "\n", + "def parse_input(input):\n", + " if Path(input).exists():\n", + " with open(input) as f:\n", + " input = f.read()\n", + " patterns = []\n", + " towels = []\n", + " for line in input.strip().split(\"\\n\"):\n", + " if \",\" in line:\n", + " towels = line.split(\", \")\n", + " elif line == \"\":\n", + " continue\n", + " else:\n", + " patterns.append(line)\n", + " return towels, patterns\n", + "\n", + "\n", + "parse_input(example)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def is_pattern_possible(towels, pattern):\n", + " if pattern == \"\":\n", + " return True\n", + " \n", + " possible_towels = [towel for towel in towels if pattern.startswith(towel)]\n", + " matching_patterns = [pattern[len(towel):] for towel in possible_towels]\n", + "\n", + " if matching_patterns:\n", + " return any(is_pattern_possible(towels, pattern) for pattern in matching_patterns)\n", + " else:\n", + " return False\n", + "\n", + "def part_one(input):\n", + " towels, patterns = parse_input(input)\n", + " return sum(is_pattern_possible(towels, pattern) for pattern in patterns)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✔️ That's right! The answer is 4.\n" + ] + } + ], + "source": [ + "verify_answer(part_one, example, 4)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✔️ That's right! The answer is 6.\n" + ] + } + ], + "source": [ + "verify_answer(part_one, example_input_file, 6)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 57 ms, sys: 3.59 ms, total: 60.6 ms\n", + "Wall time: 92.3 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "296" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time part_one(input_file)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> That's the right answer! You are one gold star ⭐ closer to finding the Chief Historian." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Part 2: All the patterns!\n", + "\n", + "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.\n", + "\n", + "Since this time we need to count all the possible patterns, we need to be more careful about the " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def all_possible_patterns(towels, pattern, cache=None):\n", + " # create a cache to store the results of already calculated patterns\n", + " # this is created here instead of cache={} in the function signature\n", + " # to avoid mutable default arguments\n", + " if cache is None:\n", + " cache = {}\n", + "\n", + " # if we have already calculated the result for this pattern, return it\n", + " if pattern in cache:\n", + " return cache[pattern]\n", + "\n", + " # if the pattern is empty, we have found a valid pattern\n", + " if pattern == \"\":\n", + " return 1 \n", + "\n", + " possible_towels = [towel for towel in towels if pattern.startswith(towel)]\n", + " matching_patterns = [pattern[len(towel) :] for towel in possible_towels]\n", + "\n", + " if matching_patterns:\n", + " result = sum(\n", + " all_possible_patterns(towels, subpattern, cache)\n", + " for subpattern in matching_patterns\n", + " )\n", + " else:\n", + " result = 0\n", + "\n", + " # cache the result to only test subpatterns once\n", + " cache[pattern] = result\n", + "\n", + " return result\n", + "\n", + "\n", + "def part_two(input):\n", + " towels, patterns = parse_input(input)\n", + " return sum(all_possible_patterns(towels, pattern) for pattern in patterns)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✔️ That's right! The answer is 5.\n" + ] + } + ], + "source": [ + "verify_answer(part_two, example, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "✔️ That's right! The answer is 16.\n" + ] + } + ], + "source": [ + "verify_answer(part_two, example_input_file, 16)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: user 110 ms, sys: 3.95 ms, total: 114 ms\n", + "Wall time: 150 ms\n" + ] + }, + { + "data": { + "text/plain": [ + "619970556776002" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%time part_two(input_file)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "> That's the right answer! You are one gold star ⭐ closer to finding the Chief Historian." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "aoc2024", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/README.md b/README.md index 60c5236..8958e60 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ For an overview of my journey and insights, check out my [blog post on AoC 2024] |16| [📄 HTML](http://kasia.codes/resources/aoc/2024/day_16) · [📓 Notebook](2024/day_16/notebook.ipynb) | ⭐ | ⭐ | |17| [📄 HTML](http://kasia.codes/resources/aoc/2024/day_17) · [📓 Notebook](2024/day_17/notebook.ipynb) | ⭐ | ⭐ | |18| [📄 HTML](http://kasia.codes/resources/aoc/2024/day_18) · [📓 Notebook](2024/day_18/notebook.ipynb) | ⭐ | ⭐ | +|19| [📄 HTML](http://kasia.codes/resources/aoc/2024/day_19) · [📓 Notebook](2024/day_19/notebook.ipynb) | ⭐ | ⭐ |

2023