Skip to content

Commit f2e2abd

Browse files
committed
Solve 2024 day 21 part 1
1 parent 1c3f21f commit f2e2abd

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
593A
2+
508A
3+
386A
4+
459A
5+
246A
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package eu.sim642.adventofcode2024
2+
3+
import eu.sim642.adventofcodelib.Grid
4+
import eu.sim642.adventofcodelib.graph.{BFS, GraphSearch, Heuristic, UnitNeighbors}
5+
import eu.sim642.adventofcodelib.pos.Pos
6+
import eu.sim642.adventofcodelib.GridImplicits.*
7+
8+
object Day21 {
9+
10+
type Code = String
11+
12+
private val numericKeypad: Grid[Char] = Vector(
13+
Vector('7', '8', '9'),
14+
Vector('4', '5', '6'),
15+
Vector('1', '2', '3'),
16+
Vector(' ', '0', 'A'),
17+
)
18+
19+
private val directionalKeypad: Grid[Char] = Vector(
20+
Vector(' ', '^', 'A'),
21+
Vector('<', 'v', '>'),
22+
)
23+
24+
private val directionalOffsets = Map(
25+
'^' -> Pos(0, -1),
26+
'>' -> Pos(1, 0),
27+
'v' -> Pos(0, 1),
28+
'<' -> Pos(-1, 0),
29+
)
30+
31+
case class State(directionalPoss: List[Pos], numericPos: Pos, input: Code) {
32+
33+
def numericPress(button: Char): Option[State] = button match {
34+
case 'A' =>
35+
val newButton = numericKeypad(numericPos)
36+
Some(copy(input = input + newButton))
37+
case _ =>
38+
val offset = directionalOffsets(button)
39+
val newNumericPos = numericPos + offset
40+
if (numericKeypad.containsPos(newNumericPos) && numericKeypad(newNumericPos) != ' ')
41+
Some(copy(numericPos = newNumericPos))
42+
else
43+
None // out of keypad
44+
}
45+
46+
def directionalPress(button: Char): Option[State] = directionalPoss match {
47+
case Nil => numericPress(button)
48+
case directionalPos :: newDirectionalPoss =>
49+
button match {
50+
case 'A' =>
51+
val newButton = directionalKeypad(directionalPos)
52+
copy(directionalPoss = newDirectionalPoss).directionalPress(newButton).map(newState =>
53+
newState.copy(directionalPoss = directionalPos :: newState.directionalPoss)
54+
)
55+
case _ =>
56+
val offset = directionalOffsets(button)
57+
val newDirectionalPos = directionalPos + offset
58+
if (directionalKeypad.containsPos(newDirectionalPos) && directionalKeypad(newDirectionalPos) != ' ')
59+
Some(copy(directionalPoss = newDirectionalPos :: newDirectionalPoss))
60+
else
61+
None // out of keypad
62+
}
63+
}
64+
65+
def userPress(button: Char): Option[State] = directionalPress(button)
66+
}
67+
68+
def shortestSequenceLength(code: Code): Int = {
69+
70+
val graphSearch = new GraphSearch[State] with UnitNeighbors[State] {
71+
override val startNode: State = State(List.fill(2)(directionalKeypad.posOf('A')), numericKeypad.posOf('A'), "")
72+
73+
override def unitNeighbors(state: State): IterableOnce[State] = "<v>^A".iterator.flatten(state.userPress).filter(s => code.startsWith(s.input))
74+
75+
override def isTargetNode(state: State, dist: Int): Boolean = state.input == code
76+
}
77+
78+
BFS.search(graphSearch).target.get._2
79+
}
80+
81+
def codeComplexity(code: Code): Int = {
82+
val numericPart = code.dropRight(1).toInt
83+
shortestSequenceLength(code) * numericPart
84+
}
85+
86+
def sumCodeComplexity(codes: Seq[Code]): Int = codes.map(codeComplexity).sum
87+
88+
def parseCodes(input: String): Seq[Code] = input.linesIterator.toSeq
89+
90+
lazy val input: String = scala.io.Source.fromInputStream(getClass.getResourceAsStream("day21.txt")).mkString.trim
91+
92+
def main(args: Array[String]): Unit = {
93+
println(sumCodeComplexity(parseCodes(input)))
94+
}
95+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package eu.sim642.adventofcode2024
2+
3+
import Day21._
4+
import org.scalatest.funsuite.AnyFunSuite
5+
6+
class Day21Test extends AnyFunSuite {
7+
8+
val exampleInput =
9+
"""029A
10+
|980A
11+
|179A
12+
|456A
13+
|379A""".stripMargin
14+
15+
test("Part 1 examples") {
16+
assert(sumCodeComplexity(parseCodes(exampleInput)) == 126384)
17+
}
18+
19+
test("Part 1 input") {
20+
assert(sumCodeComplexity(parseCodes(input)) == 157892)
21+
}
22+
}

0 commit comments

Comments
 (0)