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