@@ -12,14 +12,14 @@ import inputs.Input.loadFileSync
1212def 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
1717Licensed under the Apache License, Version 2.0 (the "License");
1818you may not use this file except in compliance with the License.
1919You may obtain a copy of the License at
2020
2121 http://www.apache.org/licenses/LICENSE-2.0
22-
22+
2323Unless required by applicable law or agreed to in writing, software
2424distributed under the License is distributed on an "AS IS" BASIS,
2525WITHOUT 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
123135def 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