diff --git a/2024/src/main/java/me/zodac/advent/Day19.java b/2024/src/main/java/me/zodac/advent/Day19.java
new file mode 100644
index 0000000..6695115
--- /dev/null
+++ b/2024/src/main/java/me/zodac/advent/Day19.java
@@ -0,0 +1,90 @@
+/*
+ * BSD Zero Clause License
+ *
+ * Copyright (c) 2021-2024 zodac.me
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package me.zodac.advent;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Solution for 2024, Day 19.
+ *
+ * @see [2024: 19] Linen Layout
+ */
+public final class Day19 {
+
+ private Day19() {
+
+ }
+
+ /**
+ * Given a {@link List} of values (representing a final 'design'), and a {@link List} of keys (representing a single 'towel' which is part of the
+ * full design), find which values have a possible valid combination from the provided keys.
+ *
+ * @param towels the input keys (towels to make the designs)
+ * @param finalDesigns the input values (designs)
+ * @return the number of valid designs that can be created
+ */
+ public static long countPossibleDesigns(final List towels, final List finalDesigns) {
+ return finalDesigns
+ .stream()
+ .filter(design -> hasDesignAnyValidCombinations(design, towels))
+ .count();
+ }
+
+ /**
+ * Given a {@link List} of values (representing a final 'design'), and a {@link List} of keys (representing a single 'towel' which is part of the
+ * full design), find which values have a possible valid combination from the provided keys. For each of these, find the total number of possible
+ * combinations of towels that can make the design.
+ *
+ * @param towels the input keys (towels to make the designs)
+ * @param finalDesigns the input values (designs)
+ * @return the total number of towel combinations to create valid designs
+ */
+ public static long countAllCombinationsOfValidDesigns(final List towels, final List finalDesigns) {
+ return finalDesigns
+ .stream()
+ .filter(design -> hasDesignAnyValidCombinations(design, towels))
+ .mapToLong(design -> countCombinationsForDesign(design, towels, new HashMap<>()))
+ .sum();
+ }
+
+ private static boolean hasDesignAnyValidCombinations(final String value, final List keys) {
+ return keys
+ .stream()
+ .anyMatch(key -> value.equals(key) || (value.startsWith(key) && hasDesignAnyValidCombinations(value.substring(key.length()), keys)));
+ }
+
+ private static long countCombinationsForDesign(final String design, final List towels, final Map cache) {
+ if (cache.containsKey(design)) {
+ return cache.get(design);
+ }
+
+ long numberOfCombinations = 0L;
+ for (final String towel : towels) {
+ if (design.equals(towel)) {
+ numberOfCombinations++;
+ } else if (design.startsWith(towel)) {
+ numberOfCombinations += countCombinationsForDesign(design.substring(towel.length()), towels, cache);
+ }
+ }
+
+ cache.put(design, numberOfCombinations);
+ return numberOfCombinations;
+ }
+}
diff --git a/2024/src/test/java/me/zodac/advent/Day19Test.java b/2024/src/test/java/me/zodac/advent/Day19Test.java
new file mode 100644
index 0000000..9bad13d
--- /dev/null
+++ b/2024/src/test/java/me/zodac/advent/Day19Test.java
@@ -0,0 +1,92 @@
+/*
+ * BSD Zero Clause License
+ *
+ * Copyright (c) 2021-2024 zodac.me
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package me.zodac.advent;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+import me.zodac.advent.input.InputReader;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests to verify answers for {@link Day19}.
+ */
+class Day19Test {
+
+ private static final String INPUT_FILENAME = "day19.txt";
+
+ @Test
+ void example() {
+ final List> valuesRaw = InputReader
+ .forExample(INPUT_FILENAME)
+ .asStrings()
+ .grouped()
+ .byDelimiter(String::isBlank);
+
+ final List keys = parseKeys(valuesRaw);
+ final List values = valuesRaw.getLast();
+
+ final long part1Result = Day19.countPossibleDesigns(keys, values);
+ assertThat(part1Result)
+ .isEqualTo(6L);
+
+ final long part2Result = Day19.countAllCombinationsOfValidDesigns(keys, values);
+ assertThat(part2Result)
+ .isEqualTo(16L);
+ }
+
+ @Test
+ void part1() {
+ final List> valuesRaw = InputReader
+ .forPuzzle(INPUT_FILENAME)
+ .asStrings()
+ .grouped()
+ .byDelimiter(String::isBlank);
+
+ final List keys = parseKeys(valuesRaw);
+ final List values = valuesRaw.getLast();
+
+ final long part1Result = Day19.countPossibleDesigns(keys, values);
+ assertThat(part1Result)
+ .isEqualTo(263L);
+ }
+
+ @Test
+ void part2() {
+ final List> valuesRaw = InputReader
+ .forPuzzle(INPUT_FILENAME)
+ .asStrings()
+ .grouped()
+ .byDelimiter(String::isBlank);
+
+ final List keys = parseKeys(valuesRaw);
+ final List values = valuesRaw.getLast();
+
+ final long part2Result = Day19.countAllCombinationsOfValidDesigns(keys, values);
+ assertThat(part2Result)
+ .isEqualTo(723_524_534_506_343L);
+ }
+
+ private static List parseKeys(List> valuesRaw) {
+ final String[] keys = valuesRaw.getFirst().getFirst().split(", ");
+ return new ArrayList<>(Arrays.asList(keys)).stream().sorted(Comparator.comparingInt(String::length).reversed()).toList();
+ }
+}
diff --git a/2024/src/test/resources/day19.txt b/2024/src/test/resources/day19.txt
new file mode 100644
index 0000000..ad43a74
--- /dev/null
+++ b/2024/src/test/resources/day19.txt
@@ -0,0 +1,10 @@
+r, wr, b, g, bwu, rb, gb, br
+
+brwrr
+bggr
+gbbr
+rrbgbr
+ubwu
+bwurrg
+brgr
+bbrgwb
\ No newline at end of file
diff --git a/README.md b/README.md
index d26c5fe..c8af644 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Advent Of Code: Java Edition
-![2024](https://img.shields.io/badge/2024%20⭐-30-yellow)
+![2024](https://img.shields.io/badge/2024%20⭐-32-yellow)
![2023](https://img.shields.io/badge/2023%20⭐-32-yellow)
![2022](https://img.shields.io/badge/2022%20⭐-28-yellow)
![2021](https://img.shields.io/badge/2021%20⭐-19-orange)
@@ -76,6 +76,7 @@ The source code is released under the [BSD Zero Clause License](https://opensour
| [Day 16](https://adventofcode.com/2024/day/16) | | |
| [Day 17](https://adventofcode.com/2024/day/17) | | |
| [Day 18](https://adventofcode.com/2024/day/18) | 22,587 ⭐ | 21,997 ⭐ |
+| [Day 19](https://adventofcode.com/2024/day/19) | 7,778 ⭐ | 6,372 ⭐ |
diff --git a/advent-of-code-inputs b/advent-of-code-inputs
index b9bd7d9..6464374 160000
--- a/advent-of-code-inputs
+++ b/advent-of-code-inputs
@@ -1 +1 @@
-Subproject commit b9bd7d95212b85399aa534e9e789d77bc8c32927
+Subproject commit 6464374c54d0c7e70392a3473718ab32da7fd9a7
diff --git a/create.sh b/create.sh
index ec32739..c03d301 100644
--- a/create.sh
+++ b/create.sh
@@ -102,11 +102,11 @@ function main() {
fi
echo "${output}" | head -n -1 > "./advent-of-code-inputs/${year}/day${day_long}.txt" || exit 1
- cd ./advent-of-code-inputs || exit 1
- git add "${year}/day${day_long}.txt"
- git commit --quiet -m "Adding input for ${year}, Day ${day_long}"
- git push origin HEAD:main --quiet
- cd .. || exit 1
+# cd ./advent-of-code-inputs || exit 1
+# git add "${year}/day${day_long}.txt"
+# git commit --quiet -m "Adding input for ${year}, Day ${day_long}"
+# git push origin HEAD:main --quiet
+# cd .. || exit 1
fi
# Example input file