Skip to content

Commit bf4b866

Browse files
committed
day 19, update README
1 parent 9b57380 commit bf4b866

File tree

9 files changed

+196
-0
lines changed

9 files changed

+196
-0
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,14 @@ went with the right solution from the start.
9898
The biggest stumbling block was adapting the well-known algorithm (I won't give it away here,
9999
it's in the code) to handle the direction of the reindeer in the maze, and accounting for that
100100
in the cost of the weighted edges of the graph.
101+
102+
### 12/19/2024
103+
104+
Day 17 part 2 was... oof. I skipped it immediately, since I didn't want to get stuck
105+
on how to "reverse" those operations, which I think is how I should approach it. It'll require
106+
a longer block of time on another day.
107+
108+
Considering how hard the problems are getting, I solved days 18 and 19 pretty damn quickly.
109+
Both of those were the ideal programming puzzles, in my view. Apply the right algorithms for
110+
both parts and you're done. No tricks in the input data, no having to be clever about what
111+
the rules imply and calculating a "shortcut" solution. Just nice efficient coding.
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.codefork.aoc2024.day19;
2+
3+
import java.util.Arrays;
4+
import java.util.HashMap;
5+
import java.util.HashSet;
6+
import java.util.Map;
7+
import java.util.Set;
8+
import java.util.stream.Collectors;
9+
import java.util.stream.Stream;
10+
11+
import static com.codefork.aoc2024.util.FoldLeft.foldLeft;
12+
13+
public record Onsen(Set<String> towels, Set<String> designs) {
14+
15+
public static Onsen create(Stream<String> data) {
16+
return data.collect(foldLeft(
17+
() -> new Onsen(new HashSet<>(), new HashSet<>()),
18+
(acc, line) -> {
19+
if (line.contains(",")) {
20+
var parts = Arrays.stream(line.split(","))
21+
.map(String::strip)
22+
.collect(Collectors.toSet());
23+
return new Onsen(parts, acc.designs());
24+
} else if (!line.isEmpty()) {
25+
acc.designs().add(line);
26+
return acc;
27+
} else {
28+
return acc;
29+
}
30+
})
31+
);
32+
}
33+
34+
/**
35+
* this part 1 solution could be rewritten to use part 2's countTowelCombinations()
36+
* to unify the code, but it's nice to see how I approached it differently for the
37+
* two parts
38+
*/
39+
public boolean isDesignPossible(String design) {
40+
if (design.isEmpty()) {
41+
return true;
42+
}
43+
var towelMatches = towels.stream()
44+
.filter(design::startsWith)
45+
.toList();
46+
return towelMatches.stream().anyMatch(towel ->
47+
isDesignPossible(design.substring(towel.length()))
48+
);
49+
}
50+
51+
/**
52+
* returns the number of designs that are actually viable (that
53+
* can be achieved with some combination of towels)
54+
*/
55+
public int countViableDesigns() {
56+
return (int) designs().stream()
57+
.filter(this::isDesignPossible)
58+
.count();
59+
}
60+
61+
/**
62+
* count the number of towel combinations that exist for a design
63+
*/
64+
public long countTowelCombinations(String design, Map<String, Long> cache) {
65+
if(cache.containsKey(design)) {
66+
return cache.get(design);
67+
}
68+
if (design.isEmpty()) {
69+
return 1;
70+
}
71+
var towelMatches = towels.stream()
72+
.filter(design::startsWith)
73+
.toList();
74+
return towelMatches.stream().reduce(0L,
75+
(acc, towel) -> {
76+
var designRemainder = design.substring(towel.length());
77+
// a lot of these calls will be repeats for the same designRemainder,
78+
// so cache and re-use that count. without caching, this algorithm won't
79+
// finish, there's too many combinations to actually compute
80+
var count = countTowelCombinations(designRemainder, cache);
81+
cache.put(designRemainder, count);
82+
return acc + count;
83+
},
84+
Long::sum);
85+
}
86+
87+
public long countTowelCombinationsForAllDesigns() {
88+
return designs().stream()
89+
.reduce(
90+
0L,
91+
(acc, design) ->
92+
acc + countTowelCombinations(design, new HashMap<>()),
93+
Long::sum);
94+
}
95+
96+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.codefork.aoc2024.day19;
2+
3+
import com.codefork.aoc2024.Problem;
4+
import com.codefork.aoc2024.util.Assert;
5+
6+
import java.util.stream.Stream;
7+
8+
public class Part01 extends Problem {
9+
10+
public String solve(Stream<String> data) {
11+
var onsen = Onsen.create(data);
12+
return String.valueOf(onsen.countViableDesigns());
13+
}
14+
15+
@Override
16+
public String solve() {
17+
Assert.assertEquals("6", solve(getSampleInput()));
18+
return solve(getInput());
19+
}
20+
21+
public static void main(String[] args) {
22+
new Part01().run();
23+
}
24+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.codefork.aoc2024.day19;
2+
3+
import com.codefork.aoc2024.Problem;
4+
import com.codefork.aoc2024.util.Assert;
5+
6+
import java.util.stream.Stream;
7+
8+
public class Part02 extends Problem {
9+
10+
public String solve(Stream<String> data) {
11+
var onsen = Onsen.create(data);
12+
return String.valueOf(onsen.countTowelCombinationsForAllDesigns());
13+
}
14+
15+
@Override
16+
public String solve() {
17+
Assert.assertEquals("16", solve(getSampleInput()));
18+
return solve(getInput());
19+
}
20+
21+
public static void main(String[] args) {
22+
new Part02().run();
23+
}
24+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.codefork.aoc2024.day20;
2+
3+
public class Part01 {
4+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.codefork.aoc2024.util;
2+
3+
import com.codefork.aoc2024.day20.Part01;
4+
5+
import java.util.HashSet;
6+
import java.util.function.BiFunction;
7+
import java.util.function.Supplier;
8+
import java.util.stream.Stream;
9+
10+
import static com.codefork.aoc2024.util.FoldLeft.foldLeft;
11+
12+
public class Grid {
13+
14+
public static <T> T parse(
15+
Stream<String> data,
16+
Supplier<T> accInitializer,
17+
BiFunction<T, String, T> reducer) {
18+
return data
19+
.map(WithIndex.indexed())
20+
.filter(lineWithIndex -> !lineWithIndex.value().isEmpty())
21+
.collect(foldLeft(
22+
() -> accInitializer.get(),
23+
(acc, lineWithIndex) -> {
24+
var y = lineWithIndex.index();
25+
var line = lineWithIndex.value();
26+
27+
return line.chars()
28+
.boxed()
29+
.map(ch -> String.valueOf((char) ch.intValue()))
30+
.collect(foldLeft(
31+
() -> acc,
32+
(acc2, ch) -> reducer.apply(acc2, ch)
33+
));
34+
})
35+
);
36+
}
37+
}

src/main/resources/day19/input

23 KB
Binary file not shown.

src/main/resources/day19/sample

99 Bytes
Binary file not shown.

src/main/resources/day20/sample

22 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)