Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e66dd8b

Browse files
committed
Clean up 2024 day 21 solutions
1 parent 0fe17f7 commit e66dd8b

File tree

1 file changed

+30
-25
lines changed

1 file changed

+30
-25
lines changed

‎src/main/scala/eu/sim642/adventofcode2024/Day21.scala

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package eu.sim642.adventofcode2024
22

33
import eu.sim642.adventofcodelib.Grid
4-
import eu.sim642.adventofcodelib.graph.{BFS, GraphSearch, GraphTraversal, Heuristic, SimultaneousBFS, TargetNode, UnitNeighbors}
5-
import eu.sim642.adventofcodelib.pos.Pos
64
import eu.sim642.adventofcodelib.GridImplicits.*
75
import eu.sim642.adventofcodelib.box.Box
6+
import eu.sim642.adventofcodelib.graph.*
7+
import eu.sim642.adventofcodelib.pos.Pos
88

99
import scala.collection.mutable
1010

@@ -46,7 +46,7 @@ object Day21 {
4646

4747
case class State(directionalPoss: List[Pos], numericPos: Pos, input: Code) {
4848

49-
def numericPress(button: Char): Option[State] = button match {
49+
privatedef numericPress(button: Char): Option[State] = button match {
5050
case 'A' =>
5151
val newButton = numericKeypad(numericPos)
5252
Some(copy(input = input + newButton))
@@ -59,7 +59,7 @@ object Day21 {
5959
None // out of keypad
6060
}
6161

62-
def directionalPress(button: Char): Option[State] = directionalPoss match {
62+
privatedef directionalPress(button: Char): Option[State] = directionalPoss match {
6363
case Nil => numericPress(button)
6464
case directionalPos :: newDirectionalPoss =>
6565
button match {
@@ -84,9 +84,11 @@ object Day21 {
8484
override def shortestSequenceLength(code: Code, directionalKeypads: Int): Long = {
8585

8686
val graphSearch = new GraphSearch[State] with UnitNeighbors[State] {
87-
override val startNode: State = State(List.fill(directionalKeypads)(directionalKeypad.posOf('A')), numericKeypad.posOf('A'), "")
87+
override val startNode: State =
88+
State(List.fill(directionalKeypads)(directionalKeypad.posOf('A')), numericKeypad.posOf('A'), "")
8889

89-
override def unitNeighbors(state: State): IterableOnce[State] = "<v>^A".iterator.flatten(state.userPress).filter(s => code.startsWith(s.input))
90+
override def unitNeighbors(state: State): IterableOnce[State] =
91+
"<v>^A".iterator.flatten(state.userPress).filter(newState => code.startsWith(newState.input))
9092

9193
override def isTargetNode(state: State, dist: Int): Boolean = state.input == code
9294
}
@@ -110,14 +112,18 @@ object Day21 {
110112
}
111113
}
112114

115+
private val offsetDirectionals: Map[Pos, Char] = directionalOffsets.map(_.swap)
116+
113117
private def keypadPaths(keypad: Grid[Char]): Map[(Char, Char), Set[Code]] = {
114118
val box = Box(Pos.zero, Pos(keypad(0).size - 1, keypad.size - 1))
119+
// TODO: use one traversal per position (to all other positions), instead of pairwise
115120
(for {
116121
startPos <- box.iterator
117122
if keypad(startPos) != ' '
118123
targetPos <- box.iterator
119124
if keypad(targetPos) != ' '
120125
} yield {
126+
// TODO: use multi-predecessor BFS to construct all shortest paths
121127
val graphSearch = new GraphSearch[Pos] with UnitNeighbors[Pos] with TargetNode[Pos] {
122128
override val startNode: Pos = startPos
123129

@@ -132,7 +138,7 @@ object Day21 {
132138
.filter(_.head == targetPos)
133139
.map(poss =>
134140
(poss lazyZip poss.tail)
135-
.map({ case(p2, p1) => directionalOffsets.find(_._2 ==p1 - p2).get._1 })
141+
.map({ (p2, p1) => offsetDirectionals(p1 - p2) })
136142
.mkString
137143
)
138144
.toSet
@@ -142,26 +148,25 @@ object Day21 {
142148
private val numericPaths: Map[(Char, Char), Set[Code]] = keypadPaths(numericKeypad)
143149
private val directionalPaths: Map[(Char, Char), Set[Code]] = keypadPaths(directionalKeypad)
144150

151+
def keypadPaths(keypad: Int): Map[(Char, Char), Set[Code]] = if (keypad == 0) numericPaths else directionalPaths
152+
145153
override def shortestSequenceLength(code: Code, directionalKeypads: Int): Long = {
146154
val memo = mutable.Map.empty[(Code, Int), Long]
147155

148-
def helper(code: Code, i: Int): Long = {
149-
memo.getOrElseUpdate((code, i), {
150-
//assert(directionalKeypads == 0)
151-
code.foldLeft(('A', 0L))({ case ((prev, length), cur) =>
152-
val newLength =
153-
(for {
154-
path <- if (i == 0) numericPaths((prev, cur)) else directionalPaths((prev, cur))
155-
path2 = path + 'A'
156-
len =
157-
if (i == directionalKeypads)
158-
path2.length.toLong
159-
else
160-
helper(path2, i + 1)
161-
} yield len).min
162-
(cur, length + newLength)
163-
})._2
164-
})
156+
def helper(code: Code, keypad: Int): Long = {
157+
if (keypad == directionalKeypads + 1)
158+
code.length
159+
else {
160+
memo.getOrElseUpdate((code, keypad), {
161+
(("A" + code) lazyZip code) // start moving from A
162+
.map({ (prev, cur) =>
163+
keypadPaths(keypad)((prev, cur))
164+
.map(path => helper(path + 'A', keypad + 1)) // end at A to press
165+
.min
166+
})
167+
.sum
168+
})
169+
}
165170
}
166171

167172
helper(code, 0)
@@ -176,7 +181,7 @@ object Day21 {
176181
val part2DirectionalKeypads = 25
177182

178183
def main(args: Array[String]): Unit = {
179-
import DynamicProgrammingSolution._
184+
import DynamicProgrammingSolution.*
180185
println(sumCodeComplexity(parseCodes(input), part1DirectionalKeypads))
181186
println(sumCodeComplexity(parseCodes(input), part2DirectionalKeypads))
182187

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /