Skip to content

Commit af9e41c

Browse files
authored
Utilize Arrow memoization (#285)
It's not strictly faster than my own cache; but I hadn't used it before, so wanted to give it a try. It's quite simple!
1 parent 97bd23a commit af9e41c

File tree

1 file changed

+23
-34
lines changed
  • src/main/kotlin/me/peckb/aoc/_2024/calendar/day21

1 file changed

+23
-34
lines changed
Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package me.peckb.aoc._2024.calendar.day21
22

3-
import javax.inject.Inject
3+
import arrow.core.memoize
44
import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory
55
import java.util.LinkedList
6+
import javax.inject.Inject
67
import kotlin.math.sign
78

89
class Day21 @Inject constructor(
@@ -18,76 +19,72 @@ class Day21 @Inject constructor(
1819

1920
private fun solve(input: Sequence<String>, indirections: Int) : Long {
2021
return input.sumOf { code ->
21-
val length = getLength(NUMERIC_PAD, code, indirections)
22+
val length = memoizedGetLength(NUMERIC_PAD, code, indirections)
2223
val size = code.take(3).toLong()
2324

2425
length * size
2526
}
2627
}
2728

28-
private fun getLength(keyPad: Map<Char, Pair<Int, Int>>, code: String, indirections: Int): Long {
29-
val lengthKey = LengthKey(keyPad.size, code, indirections)
29+
private val memoizedGetLength = ::getLength.memoize()
3030

31-
if (LENGTH_CACHE.containsKey(lengthKey)) {
32-
return LENGTH_CACHE[lengthKey]!!
33-
}
31+
private val memoizedPaths = ::paths.memoize()
3432

33+
private fun getLength(keyPad: Map<Char, Pair<Int, Int>>, code: String, indirections: Int): Long {
3534
if (indirections == 0) {
36-
return code.length.toLong().also { LENGTH_CACHE[lengthKey] = it }
35+
return code.length.toLong()
3736
}
3837

3938
val minLength = "A${code}".toList()
4039
.windowed(2)
4140
.sumOf { (s, e) ->
42-
paths(keyPad, s, e).minOf { getLength(DIRECTION_PAD, "${it}A", indirections - 1) }
41+
memoizedPaths(keyPad, s, e).minOf { memoizedGetLength(DIRECTION_PAD, "${it}A", indirections - 1) }
4342
}
4443

45-
return minLength.also { LENGTH_CACHE[lengthKey] = it }
44+
return minLength
4645
}
4746

4847
private fun paths(keyPad: Map<Char, Pair<Int, Int>>, start: Char, end: Char): List<String> {
49-
val pathKey = PathKey(keyPad.size, start, end)
50-
51-
if (PATHS_CACHE.containsKey(pathKey)) {
52-
return PATHS_CACHE[pathKey]!!
53-
}
54-
5548
val paths = mutableListOf<String>()
49+
5650
val partialPaths = LinkedList<Pair<Pair<Int, Int>, String>>().apply {
5751
add(keyPad[start]!! to "")
5852
}
5953

6054
pathGeneration@ while(partialPaths.isNotEmpty()) {
6155
val (position, path) = partialPaths.poll()
62-
6356
val target = keyPad[end]!!
57+
6458
if (position == target) {
6559
paths.add(path)
6660
continue@pathGeneration
6761
}
6862

69-
val yDelta = target.second - position.second
70-
if (yDelta != 0) {
71-
val newPoint = position.first to position.second + yDelta.sign
63+
val (yDelta, xDelta) = target - position
64+
65+
if (xDelta != 0) {
66+
val newPoint = position.first to position.second + xDelta.sign
7267
if (newPoint in keyPad.values) {
73-
val dir = if (yDelta > 0) { ">" } else { "<" }
68+
val dir = if (xDelta > 0) { ">" } else { "<" }
7469
partialPaths.add(newPoint to "$path$dir")
7570
}
7671
}
7772

78-
val xDelta = target.first - position.first
79-
if (xDelta != 0) {
80-
val newPoint = position.first + xDelta.sign to position.second
73+
if (yDelta != 0) {
74+
val newPoint = position.first + yDelta.sign to position.second
8175
if (newPoint in keyPad.values) {
82-
val dir = if (xDelta > 0) { "v" } else { "^" }
76+
val dir = if (yDelta > 0) { "v" } else { "^" }
8377
partialPaths.add(newPoint to "$path$dir")
8478
}
8579
}
8680
}
8781

88-
return paths.also { PATHS_CACHE[pathKey] = it }
82+
return paths
8983
}
9084

85+
private operator fun Pair<Int, Int>.minus(other: Pair<Int, Int>) =
86+
this.first - other.first to this.second - other.second
87+
9188
companion object {
9289
val NUMERIC_PAD = mapOf(
9390
'7' to (0 to 0), '8' to (0 to 1), '9' to (0 to 2),
@@ -100,13 +97,5 @@ class Day21 @Inject constructor(
10097
'^' to (0 to 1), 'A' to (0 to 2),
10198
'<' to (1 to 0), 'v' to (1 to 1), '>' to (1 to 2)
10299
)
103-
104-
val LENGTH_CACHE = mutableMapOf<LengthKey, Long>()
105-
106-
val PATHS_CACHE = mutableMapOf<PathKey, List<String>>()
107100
}
108101
}
109-
110-
data class LengthKey(val padIndicator: Int, val pattern: String, val robots: Int)
111-
112-
data class PathKey(val padIndicator: Int, val start: Char, val end: Char)

0 commit comments

Comments
 (0)