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