Skip to content

Commit efad033

Browse files
committed
Day 22, Dijkstra
1 parent 605d301 commit efad033

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

2018/22.scala

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import scala.collection.mutable
2+
3+
case class C(x: BigInt, y: BigInt)
4+
5+
val Depth = 8112
6+
val Target = C(13, 743)
7+
val Modulo = 20183
8+
val Zero = BigInt(0)
9+
10+
def memoize[I, O](f: I => O): I => O = new mutable.HashMap[I, O]() {
11+
override def apply(key: I) = getOrElseUpdate(key, f(key))
12+
}
13+
14+
lazy val geologicIndex: C => BigInt = memoize {
15+
case C(Zero, Zero) => 0
16+
case Target => 0
17+
case C(x, Zero) => x * 16807 mod Modulo
18+
case C(Zero, y) => y * 48271 mod Modulo
19+
case C(x, y) => erosionLevel(C(x, y - 1)) * erosionLevel(C(x - 1, y))
20+
}
21+
22+
def erosionLevel(c: C) = (geologicIndex(c) + Depth) mod Modulo
23+
def riskLevel(c: C) = erosionLevel(c) mod 3
24+
25+
val minSquare = for {
26+
i <- Zero to Target.x
27+
j <- Zero to Target.y
28+
} yield C(i, j)
29+
30+
println(minSquare.map(riskLevel).sum)
31+
32+
// Part 2
33+
34+
sealed trait Tool
35+
case object Torch extends Tool
36+
case object Gear extends Tool
37+
case object Neither extends Tool
38+
case class Vertex(c: C, tool: Tool)
39+
case class VertexWithCost(v: Vertex, cost: Int) extends Ordered[VertexWithCost] {
40+
override def compare(that: VertexWithCost) = that.cost - this.cost
41+
}
42+
43+
def cost(from: Vertex, to: Vertex): Int = if (from.tool == to.tool) 1 else 7
44+
def tools(riskLevel: BigInt): Set[Tool] = riskLevel.toLong match {
45+
case 0 => Set(Torch, Gear)
46+
case 1 => Set(Gear, Neither)
47+
case 2 => Set(Torch, Neither)
48+
}
49+
def otherTool(v: Vertex): Tool = tools(riskLevel(v.c)).filterNot(_ == v.tool).head
50+
51+
def neighbours(v: Vertex): Set[Vertex] = {
52+
Seq((-1, 0), (1, 0), (0, 1), (0, -1))
53+
.map { case (dx, dy) => C(v.c.x + dx, v.c.y + dy) }
54+
.filter { case C(x, y) => x >= 0 && y >= 0 }
55+
.map { c => Vertex(c, v.tool) }
56+
.filter { c => tools(riskLevel(c.c)).contains(c.tool) }
57+
.toSet ++ Set(v.copy(tool = otherTool(v)))
58+
}
59+
60+
def shortestPath(): Unit = {
61+
val target = Vertex(Target, Torch)
62+
val start = Vertex(C(Zero, Zero), Torch)
63+
val frontier = mutable.PriorityQueue(VertexWithCost(start, 0))
64+
val distance = mutable.Map[Vertex, Int](start -> 0)
65+
val prev = mutable.Map[Vertex, Vertex]()
66+
val visited = mutable.Set[Vertex]()
67+
var enqueue = true
68+
69+
while (frontier.nonEmpty) {
70+
val u = frontier.dequeue().v
71+
if (!visited.contains(u)) {
72+
visited.add(u)
73+
neighbours(u).foreach(v => {
74+
val alt = distance(u) + cost(u, v)
75+
if (alt < distance.getOrElse(v, Int.MaxValue)) {
76+
distance.put(v, alt)
77+
prev.put(v, u)
78+
79+
if (v == target) {
80+
enqueue = false
81+
} else {
82+
if (enqueue) {
83+
frontier.enqueue(VertexWithCost(v, alt))
84+
}
85+
}
86+
}
87+
})
88+
}
89+
}
90+
91+
distance(target)
92+
}
93+
94+
shortestPath()

0 commit comments

Comments
 (0)