Skip to content

Commit 1419659

Browse files
committed
Day 21 (p2 gives incorrect solution because impl error)
Thanks to @lukebemish for posting his solution early :)
1 parent 6a4e32d commit 1419659

File tree

3 files changed

+91
-1
lines changed

3 files changed

+91
-1
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ repositories {
3030
}
3131

3232
powerAssert {
33-
// functions = listOf("kotlin.assert", "kotlin.require", "kotlin.check")
33+
functions = listOf("kotlin.assert", "kotlin.require", "kotlin.check")
3434
includedSourceSets = listOf("main")
3535
}
3636

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package sschr15.aocsolutions
2+
3+
import com.sschr15.aoc.annotations.Memoize
4+
import sschr15.aocsolutions.util.*
5+
6+
/**
7+
* AOC 2024 [Day 21](https://adventofcode.com/2024/day/21)
8+
* Challenge:
9+
*/
10+
object Day21 : Challenge {
11+
override fun solve() = challenge(2024, 21) {
12+
test()
13+
14+
// Adapted from lukebemish's solution - this puzzle completely destroyed my hopes and dreams
15+
16+
val keypad = """
17+
789
18+
456
19+
123
20+
0A
21+
""".trimIndent().lines().toGrid()
22+
val keyMap = keypad.toPointMap().map { (k, v) -> v to k }.toMap()
23+
24+
val directions = """
25+
| ^A
26+
|<v>
27+
""".trimMargin().lines().toGrid()
28+
val dirMap = directions.toPointMap().map { (k, v) -> v to k }.toMap()
29+
30+
fun getWays(start: Point, value: Char, map: Map<Char, Point>): List<String> {
31+
var reverse = true
32+
val s = buildString {
33+
val end = map[value]!!
34+
var startX = start.x
35+
if (startX == 0 && end.y == map[' ']!!.y) {
36+
if (0 < end.x) {
37+
append(">".repeat(end.x))
38+
startX = end.x
39+
}
40+
reverse = false
41+
} else if (start.y == map[' ']!!.y && end.x == 0) {
42+
reverse = false
43+
}
44+
45+
if (start.y > end.y) append("^".repeat(start.y - end.y))
46+
if (start.y < end.y) append("v".repeat(end.y - start.y))
47+
if (startX > end.x) append("<".repeat(startX - end.x))
48+
if (startX < end.x) append(">".repeat(end.x - startX))
49+
}
50+
51+
if (s == s.reversed() || !reverse) return listOf(s)
52+
return listOf(s, s.reversed())
53+
}
54+
55+
fun findWays(sequence: String, map: Map<Char, Point>): List<String> {
56+
var results = listOf(emptyList<Char>() to map['A']!!)
57+
sequence.forEach { c ->
58+
results = results.flatMap { (path, start) ->
59+
getWays(start, c, map).map { s -> (path + s.toList() + 'A') to map[c]!! }
60+
}
61+
}
62+
val min = results.minOf { (path) -> path.size }
63+
return results.mapNotNull { (path) -> path.takeIf { it.size == min }?.joinToString("") }
64+
}
65+
66+
@Memoize
67+
fun shortestDirectionalPath(iterations: Int, goal: String): Long {
68+
if (iterations == -1) return goal.length.toLong()
69+
val sections = goal.split('A').dropLastWhile { it.isEmpty() }.map { it + "A" }
70+
return sections.sumOf { section -> findWays(section, dirMap).minOf { shortestDirectionalPath(iterations - 1, it) } }
71+
}
72+
73+
fun shortestKeypadPath(iterations: Int, goal: String): Long {
74+
// Separate function to keep from memoization
75+
return findWays(goal, keyMap).minOf { shortestDirectionalPath(iterations - 1, it) }
76+
}
77+
78+
part1 {
79+
inputLines.sumOf { shortestKeypadPath(2, it) * it.takeWhile(Char::isDigit).toLong() }
80+
}
81+
part2 {
82+
inputLines.sumOf { shortestKeypadPath(25, it).also(::println) * it.takeWhile(Char::isDigit).toLong() }
83+
}
84+
}
85+
86+
@JvmStatic
87+
fun main(args: Array<String>) = println(solve())
88+
}

src/main/kotlin/sschr15/aocsolutions/util/Algo.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ fun <T> Grid<T>.aStar(
126126
start: Point,
127127
end: Point,
128128
getCost: (current: Point, neighbor: Point) -> Int,
129+
isValidPoint: (Point) -> Boolean = { true },
129130
heuristic: (current: Point, goal: Point) -> Int = AbstractPoint::manhattanDistance,
130131
): List<Point>? {
131132
data class Node(val point: Point, val f: Int)
@@ -146,6 +147,7 @@ fun <T> Grid<T>.aStar(
146147

147148
for (neighbor in current.neighborsIn(this)) {
148149
if (neighbor in closed) continue
150+
if (!isValidPoint(neighbor)) continue
149151

150152
val tentativeGScore = gScore[current] + getCost(current, neighbor)
151153
if (tentativeGScore < gScore[neighbor]) {

0 commit comments

Comments
 (0)