Skip to content

Commit c6d03ed

Browse files
committed
Experimentally implement Dijkstra search for day 21
1 parent 209af13 commit c6d03ed

File tree

1 file changed

+82
-9
lines changed

1 file changed

+82
-9
lines changed

day21/src/day21.scala

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,92 @@
11
import scala.io.Source
2+
import scala.collection.mutable
23

3-
case class Vec2(x: Int, y: Int)
4+
case class Vec2(x: Int, y: Int) {
5+
def neighbors = (-1 to 1).flatMap { dy =>
6+
(-1 to 1)
7+
.filter { dx => (dx != 0) ^ (dy != 0) }
8+
.map { dx => new Vec2(x + dx, y + dy) }
9+
}
410

5-
val NUMPAD = Map(
6-
(Vec2(0, 0), '1'), (Vec2(1, 0), '2'), (Vec2(2, 0), '3'),
7-
(Vec2(0, 1), '4'), (Vec2(1, 1), '5'), (Vec2(2, 1), '6'),
8-
(Vec2(0, 2), '7'), (Vec2(1, 2), '7'), (Vec2(2, 2), '8'),
9-
(Vec2(1, 3), '9'), (Vec2(2, 3), 'A'),
11+
def +(that: Vec2) = Vec2(x + that.x, y + that.y)
12+
}
13+
14+
enum PadType:
15+
case Num, Dir
16+
17+
val PAD_LAYOUTS = Map(
18+
(PadType.Num, Map(
19+
(Vec2(0, 0), '1'), (Vec2(1, 0), '2'), (Vec2(2, 0), '3'),
20+
(Vec2(0, 1), '4'), (Vec2(1, 1), '5'), (Vec2(2, 1), '6'),
21+
(Vec2(0, 2), '7'), (Vec2(1, 2), '7'), (Vec2(2, 2), '8'),
22+
(Vec2(1, 3), '9'), (Vec2(2, 3), 'A'),
23+
)),
24+
(PadType.Dir, Map(
25+
(Vec2(1, 0), '^'), (Vec2(2, 0), 'A'),
26+
(Vec2(0, 1), '<'), (Vec2(1, 1), 'v'), (Vec2(2, 1), '>'),
27+
)),
1028
)
1129

12-
val ARROWPAD = Map(
13-
(Vec2(1, 0), '^'), (Vec2(2, 0), 'A'),
14-
(Vec2(0, 1), '<'), (Vec2(1, 1), 'v'), (Vec2(2, 1), '>'),
30+
val DIRECTIONS = Map(
31+
('<', Vec2(-1, 0)),
32+
('>', Vec2( 1, 0)),
33+
('^', Vec2( 0, -1)),
34+
('v', Vec2( 0, 1)),
1535
)
1636

37+
val ACTIONS = List('A') ++ DIRECTIONS.keySet
38+
39+
case class Pad(ptype: PadType, pos: Vec2) {
40+
def layout = PAD_LAYOUTS(ptype)
41+
42+
def activate: Char = layout(pos)
43+
44+
def perform(action: Char): (Option[Char], Pad) =
45+
action match
46+
case 'A' => (Some(activate), this)
47+
case _ => (None, Pad(ptype, pos + DIRECTIONS(action)))
48+
}
49+
50+
case class State(pads: List[Pad], output: String) {
51+
def perform(action: Char) =
52+
val (newPads, outAction) = pads.foldLeft[(List[Pad], Option[Char])]((List(), Some(action))) { case ((pads, action), pad) =>
53+
action match
54+
case Some(action) =>
55+
val (newAction, newPad) = pad.perform(action)
56+
(pads :+ newPad, newAction)
57+
case None => (pads :+ pad, None)
58+
}
59+
val newOutput = outAction.map(output.appended(_)).getOrElse(output)
60+
State(newPads, newOutput)
61+
}
62+
63+
case class Node(state: State, program: String) extends Ordered[Node] {
64+
def compare(that: Node): Int = program.length() compare that.program.length()
65+
}
66+
67+
def shortestProgram(goal: String): String =
68+
// Your run-of-the-mill Dijkstra implementation
69+
70+
val queue = mutable.PriorityQueue[Node]()
71+
val visited = mutable.HashSet[State]()
72+
73+
val start = Node(State(List(), ""), "")
74+
queue.enqueue(start)
75+
visited.add(start.state)
76+
77+
while (!queue.isEmpty) do
78+
val node = queue.dequeue()
79+
if (node.state.output == goal) then
80+
return node.program
81+
82+
for action <- ACTIONS do
83+
val newState = node.state.perform(action)
84+
if !visited.contains(newState) then
85+
visited.add(newState)
86+
queue.enqueue(Node(newState, node.program.appended(action)))
87+
88+
throw new RuntimeException("No shortest program found")
89+
1790
@main def main(path: String) =
1891
val input = Source.fromFile(path).getLines.toList
1992
println(s"$input")

0 commit comments

Comments
 (0)