Skip to content

Commit cea56d0

Browse files
authored
Merge pull request scalacenter#261 from sjrd/make-2022-day-16-fastish-on-js
Make the day16 of 2022 implementation fastish on Scala.js.
2 parents b340794 + 1ef8699 commit cea56d0

File tree

1 file changed

+35
-23
lines changed

1 file changed

+35
-23
lines changed

2022/src/day16.scala

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import inputs.Input.loadFileSync
1212
def loadInput(): String = loadFileSync(s"$currentDir/../input/day16")
1313

1414
/*
15-
Copyright 2022 Tyler Coles (javadocmd.com) & Quentin Bernet
15+
Copyright 2022 Tyler Coles (javadocmd.com), Quentin Bernet, Sébastien Doeraene and Jamie Thompson
1616
1717
Licensed under the Apache License, Version 2.0 (the "License");
1818
you may not use this file except in compliance with the License.
1919
You may obtain a copy of the License at
2020
2121
http://www.apache.org/licenses/LICENSE-2.0
22-
22+
2323
Unless required by applicable law or agreed to in writing, software
2424
distributed under the License is distributed on an "AS IS" BASIS,
2525
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -59,7 +59,7 @@ def computeRoutes(start: Id, neighbors: Id => List[Id]): Map[Id, Int] =
5959

6060
case class State(frontier: List[(Id, Int)], scores: Map[Id, Int]):
6161

62-
private def getScore(id: Id): Int = scores.getOrElse(id, Int.MaxValue)
62+
private def getScore(id: Id): Int = scores.getOrElse(id, Int.MaxValue)
6363
private def setScore(id: Id, s: Int) = State((id, s + 1) :: frontier, scores + (id -> s))
6464

6565
def dequeued: (Id, State) =
@@ -74,7 +74,7 @@ def computeRoutes(start: Id, neighbors: Id => List[Id]): Map[Id, Int] =
7474

7575
object State:
7676
def initial(start: Id) = State(List((start, 0)), Map(start -> 0))
77-
77+
7878
def recurse(state: State): State =
7979
if state.frontier.isEmpty then
8080
state
@@ -98,27 +98,39 @@ def bestPath(map: RoomsInfo, start: Id, valves: Set[Id], timeAllowed: Int): Int
9898
// we limit our options by only considering the still-closed valves
9999
// and `valves` has already culled any room with a flow value of 0 -- no point in considering these rooms!
100100

101-
def recurse(path: List[Id], valvesLeft: Set[Id], timeLeft: Int, totalValue: Int): Int =
101+
val valvesLookup = IArray.from(valves)
102+
val valveCount = valvesLookup.size
103+
val _activeValveIndices = Array.fill[Boolean](valveCount + 1)(true) // add an extra valve for the initial state
104+
def valveIndexLeft(i: Int) = _activeValveIndices(i)
105+
def withoutValve(i: Int)(f: => Int) =
106+
try
107+
_activeValveIndices(i) = false
108+
f
109+
finally _activeValveIndices(i) = true
110+
val roomsByIndices = IArray.tabulate(valveCount)(i => map.rooms(valvesLookup(i)))
111+
112+
def recurse(hiddenValve: Int, current: Id, timeLeft: Int, totalValue: Int): Int = withoutValve(hiddenValve):
102113
// recursively consider all plausible options
103114
// we are finished when we no longer have time to reach another valve or all valves are open
104-
valvesLeft
105-
.flatMap{ id =>
106-
val current = path.head
107-
val distance = map.routes(current)(id)
108-
// how much time is left after we traverse there and open the valve?
109-
val t = timeLeft - distance - 1
110-
// if `t` is zero or less this option can be skipped
111-
Option.when(t > 0) {
112-
// the value of choosing a particular valve (over the life of our simulation)
113-
// is its flow rate multiplied by the time remaining after opening it
114-
val value = map.rooms(id).flow * t
115-
recurse(id :: path, valvesLeft - id, t, totalValue + value)
116-
}
117-
}
118-
.maxOption
119-
.getOrElse { totalValue }
115+
val routesOfCurrent = map.routes(current)
116+
var bestValue = totalValue
117+
for index <- valvesLookup.indices if valveIndexLeft(index) do
118+
val id = valvesLookup(index)
119+
val distance = routesOfCurrent(id)
120+
// how much time is left after we traverse there and open the valve?
121+
val t = timeLeft - distance - 1
122+
// if `t` is zero or less this option can be skipped
123+
if t > 0 then
124+
// the value of choosing a particular valve (over the life of our simulation)
125+
// is its flow rate multiplied by the time remaining after opening it
126+
val value = roomsByIndices(index).flow * t
127+
val recValue = recurse(hiddenValve = index, id, t, totalValue + value)
128+
if recValue > bestValue then
129+
bestValue = recValue
130+
end for
131+
bestValue
120132
end recurse
121-
recurse(start :: Nil, valves, timeAllowed, 0)
133+
recurse(valveCount, start, timeAllowed, 0)
122134

123135
def part1(input: String) =
124136
val time = 30
@@ -142,7 +154,7 @@ def part2(input: String) =
142154
// taking the best of those.
143155

144156
// we can now calculate the efforts separately and sum their values to find the best
145-
val allPaths =
157+
val allPaths =
146158
for va <- valvesA yield
147159
val vb = map.valves -- va
148160
val scoreA = bestPath(map, "AA", va, time)

0 commit comments

Comments
 (0)