-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsolution.scala
More file actions
106 lines (85 loc) · 3 KB
/
Copy pathsolution.scala
File metadata and controls
106 lines (85 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import scala.collection.mutable.Queue
import scala.collection.mutable.Set
import scala.collection.mutable.Map
import util.FileUtils
type Item = (String, String)
type Layout = List[List[Item]]
type Summarised = (Int, List[List[(String, Int)]])
class State(val floor: Int, val layout: Layout) {
def isSafe: Boolean =
// if a chip is ever left in the same area as another RTG,
// and it's not connected to its own RTG, the chip will be fried
layout.forall(floor => {
val chips = floor.filter((_, item) => item == "M")
val rtgs = floor.filter((_, item) => item == "G")
chips.forall((elem, _) => rtgs.contains((elem, "G")) || rtgs.isEmpty)
})
def isGoal: Boolean = layout.slice(0, 3).forall(_.isEmpty)
def summarised: Summarised =
(floor, layout.map(_.groupBy(_._2).mapValues(_.length).toList))
def move(items: List[Item], to: Int): State =
State(
to,
layout
.updated(floor, layout(floor).diff(items))
.updated(to, layout(to).appendedAll(items))
)
def next: Iterator[State] = {
val items = layout(floor)
(items.combinations(1) ++ items.combinations(2))
.flatMap(move => List(-1, 1).map(dir => (move, floor + dir)))
.filter((_, to) => 0 <= to && to < 4) // invalid floor
.map((items, to) => move(items, to))
.filter(_.isSafe)
}
override def toString(): String = {
s"Floor: $floor\n${layout.reverse.mkString("\n")}"
}
}
object Solution {
val itemPattern = """(\w+)( generator|-compatible microchip)""".r
def getNumSteps(initialState: State, verbose: Boolean): Int = {
val explored = Set[Summarised](initialState.summarised)
val queue = Queue[(Int, State)]((0, initialState))
val timer = Map[Int, Long]()
val startTime = System.currentTimeMillis()
while (queue.nonEmpty) {
val (numSteps, state) = queue.dequeue()
if (verbose && !timer.contains(numSteps)) {
timer(numSteps) = System.currentTimeMillis()
println(s"$numSteps: ${timer(numSteps) - startTime} ms")
}
if (state.isGoal) return numSteps
state.next.foreach((state) => {
if (!explored.contains(state.summarised)) {
explored.add(state.summarised)
queue.enqueue((numSteps + 1, state))
}
})
}
return -1
}
def main(args: Array[String]): Unit = {
val lines: List[String] = FileUtils.read(args(0))
val verbose = args.length > 1
val layout = lines.map(floor =>
itemPattern
.findAllMatchIn(floor)
.map(m => (m.group(1), if (m.group(2) == " generator") "G" else "M"))
.toList
)
val initialState = State(0, layout)
println(s"Part 1: ${getNumSteps(initialState, verbose)}")
val updatedLayout =
layout.updated(
0,
layout(0).appendedAll(
List("elerium", "dilithium").flatMap(elem =>
List("G", "M").map(t => (elem, t))
)
)
)
val updatedState = State(0, updatedLayout)
println(s"Part 2: ${getNumSteps(updatedState, verbose)}")
}
}