Skip to content

Commit

Permalink
feat: solve year 2022, day 16 (#51)
Browse files Browse the repository at this point in the history
Closes: #45
  • Loading branch information
akikanellis committed Apr 4, 2023
1 parent 89aa953 commit 633ca01
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 0 deletions.
@@ -0,0 +1,153 @@
package com.akikanellis.adventofcode.year2022

object Day16 {
private val VALVES_REGEX =
"Valve (.+) has flow rate=(.+); tunnels? leads? to valves? (.+)".toRegex()

fun highestPressureThanCanBeReleased(input: String, elephantWillHelp: Boolean) =
valves(input, elephantWillHelp)
.highestPressureThanCanBeReleased()

private fun valves(input: String, elephantWillHelp: Boolean): Valves {
val valvesToNeighbourNames = valvesToNeighbourNames(input)
val valves = valvesToNeighbourNames.map { (valve, _) -> valve }

valvesToNeighbourNames.forEach { (valve, neighbourNames) ->
valve.neighbours = valves.filter { it.name in neighbourNames }
}
valves.forEach { valve -> initialiseDistancesToOtherValves(valve) }

return Valves(
valves = valves,
elephantWillHelp = elephantWillHelp
)
}

private fun valvesToNeighbourNames(input: String) = input.lines()
.filter { it.isNotBlank() }
.map { line ->
val groupValues = VALVES_REGEX.matchEntire(line)!!.groupValues
Pair(
Valve(
name = groupValues[1],
flowRate = groupValues[2].toInt()
),
groupValues[3].split(", ")
)
}

private fun initialiseDistancesToOtherValves(valve: Valve) {
val valveNameToShortestDistance = mutableMapOf<String, Int>()
.apply { put(valve.name, 0) }

val remainingValves = mutableListOf(valve)
while (remainingValves.isNotEmpty()) {
val currentValve = remainingValves.removeFirst()
val updatedDistanceToCurrentValveNeighbours =
valveNameToShortestDistance[currentValve.name]!! + 1

currentValve.neighbours.forEach { currentValveNeighbour ->
val precomputedDistanceToCurrentValveNeighbour =
valveNameToShortestDistance.getOrDefault(
currentValveNeighbour.name,
Int.MAX_VALUE
)

if (
updatedDistanceToCurrentValveNeighbours <
precomputedDistanceToCurrentValveNeighbour
) {
valveNameToShortestDistance[currentValveNeighbour.name] =
updatedDistanceToCurrentValveNeighbours
remainingValves += currentValveNeighbour
}
}
}

valve.valveNameToShortestDistance = valveNameToShortestDistance
}

private class Valves(
val startValve: Valve,
val openableValves: Set<Valve>,
val elephantWillHelp: Boolean,
val elephantIsHelping: Boolean
) {
constructor(
valves: List<Valve>,
elephantWillHelp: Boolean
) : this(
startValve = valves.single { it.name == "AA" },
openableValves = valves.filter { it.flowRate > 0 }.toSet(),
elephantWillHelp = elephantWillHelp,
elephantIsHelping = false
)

val totalMinutes = if (elephantWillHelp || elephantIsHelping) 26 else 30
val precomputedSystemStateToMaxPressure = mutableMapOf<SystemState, Int>()

fun highestPressureThanCanBeReleased() = highestPressureThanCanBeReleased(
remainingMinutes = totalMinutes,
currentValve = startValve,
remainingValves = openableValves
)

private fun highestPressureThanCanBeReleased(
remainingMinutes: Int,
currentValve: Valve,
remainingValves: Set<Valve>
): Int {
val currentValvePressure = remainingMinutes * currentValve.flowRate
val currentSystemState = SystemState(
currentValveName = currentValve.name,
remainingMinutes = remainingMinutes,
remainingValves = remainingValves
)

return currentValvePressure + precomputedSystemStateToMaxPressure
.getOrPut(currentSystemState) {
val remainingValvesMaxPressure = remainingValves
.filter { nextValve ->
currentValve.distanceTo(nextValve) < remainingMinutes
}
.maxOfOrNull { nextValve ->
val remainingMinutesForNextValve =
remainingMinutes - currentValve.distanceTo(nextValve) - 1

highestPressureThanCanBeReleased(
remainingMinutes = remainingMinutesForNextValve,
currentValve = nextValve,
remainingValves = remainingValves - nextValve
)
} ?: 0

if (elephantWillHelp && !elephantIsHelping) {
maxOf(
remainingValvesMaxPressure,
Valves(
startValve = startValve,
openableValves = remainingValves,
elephantWillHelp = false,
elephantIsHelping = true
).highestPressureThanCanBeReleased()
)
} else {
remainingValvesMaxPressure
}
}
}
}

private data class Valve(val name: String, val flowRate: Int) {
lateinit var neighbours: List<Valve>
lateinit var valveNameToShortestDistance: Map<String, Int>

fun distanceTo(other: Valve) = valveNameToShortestDistance[other.name]!!
}

private data class SystemState(
val currentValveName: String,
val remainingMinutes: Int,
val remainingValves: Set<Valve>
)
}
@@ -0,0 +1,28 @@
package com.akikanellis.adventofcode.year2022

import com.akikanellis.adventofcode.testutils.resourceText
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import kotlin.test.assertEquals

class Day16Test {
@ParameterizedTest
@CsvSource(
"/day-16-input-example.txt, false, 1_651",
"/day-16-input-puzzle.txt, false, 1_653",
"/day-16-input-example.txt, true, 1_707",
"/day-16-input-puzzle.txt, true, 2_223"
)
fun `finds highest pressure that can be released`(
inputFile: String,
elephantWillHelp: Boolean,
expectedPressure: Int
) {
val input = resourceText(inputFile)

val highestPressureThanCanBeReleased =
Day16.highestPressureThanCanBeReleased(input, elephantWillHelp)

assertEquals(expectedPressure, highestPressureThanCanBeReleased)
}
}
10 changes: 10 additions & 0 deletions advent-of-code/src/test/resources/day-16-input-example.txt
@@ -0,0 +1,10 @@
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
Valve BB has flow rate=13; tunnels lead to valves CC, AA
Valve CC has flow rate=2; tunnels lead to valves DD, BB
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
Valve EE has flow rate=3; tunnels lead to valves FF, DD
Valve FF has flow rate=0; tunnels lead to valves EE, GG
Valve GG has flow rate=0; tunnels lead to valves FF, HH
Valve HH has flow rate=22; tunnel leads to valve GG
Valve II has flow rate=0; tunnels lead to valves AA, JJ
Valve JJ has flow rate=21; tunnel leads to valve II
64 changes: 64 additions & 0 deletions advent-of-code/src/test/resources/day-16-input-puzzle.txt
@@ -0,0 +1,64 @@
Valve MU has flow rate=0; tunnels lead to valves VT, LA
Valve TQ has flow rate=0; tunnels lead to valves HU, SU
Valve YH has flow rate=0; tunnels lead to valves CN, BN
Valve EO has flow rate=0; tunnels lead to valves IK, CN
Valve MH has flow rate=0; tunnels lead to valves GG, HG
Valve RJ has flow rate=0; tunnels lead to valves AA, RI
Valve XZ has flow rate=0; tunnels lead to valves PX, VT
Valve UU has flow rate=0; tunnels lead to valves DT, XG
Valve KV has flow rate=13; tunnels lead to valves HN, CV, PE, XD, TA
Valve SU has flow rate=19; tunnels lead to valves TQ, HF, OL, SF
Valve BB has flow rate=0; tunnels lead to valves NS, HR
Valve RI has flow rate=4; tunnels lead to valves ML, EE, TZ, RJ, PE
Valve TZ has flow rate=0; tunnels lead to valves VT, RI
Valve LY has flow rate=0; tunnels lead to valves EE, RP
Valve PX has flow rate=0; tunnels lead to valves XZ, JQ
Valve VH has flow rate=0; tunnels lead to valves DT, TA
Valve HN has flow rate=0; tunnels lead to valves KV, LR
Valve LR has flow rate=0; tunnels lead to valves HR, HN
Valve NJ has flow rate=0; tunnels lead to valves QF, JC
Valve AM has flow rate=0; tunnels lead to valves OJ, AA
Valve FM has flow rate=0; tunnels lead to valves VT, RP
Valve VT has flow rate=5; tunnels lead to valves IP, XZ, TZ, FM, MU
Valve HF has flow rate=0; tunnels lead to valves NR, SU
Valve HR has flow rate=11; tunnels lead to valves BB, KO, LR
Valve WX has flow rate=0; tunnels lead to valves CN, IP
Valve PE has flow rate=0; tunnels lead to valves KV, RI
Valve QF has flow rate=17; tunnels lead to valves YI, NJ
Valve EE has flow rate=0; tunnels lead to valves LY, RI
Valve UH has flow rate=25; tunnel leads to valve YI
Valve CV has flow rate=0; tunnels lead to valves KV, NS
Valve SF has flow rate=0; tunnels lead to valves YN, SU
Valve RP has flow rate=3; tunnels lead to valves HG, FM, OJ, IK, LY
Valve XD has flow rate=0; tunnels lead to valves IL, KV
Valve GG has flow rate=12; tunnels lead to valves ML, IL, MH, OL, KA
Valve XG has flow rate=0; tunnels lead to valves LI, UU
Valve YA has flow rate=21; tunnels lead to valves UJ, GQ
Valve OL has flow rate=0; tunnels lead to valves GG, SU
Valve AN has flow rate=0; tunnels lead to valves AA, IX
Valve LI has flow rate=15; tunnel leads to valve XG
Valve GQ has flow rate=0; tunnels lead to valves YA, KO
Valve HU has flow rate=0; tunnels lead to valves TQ, DT
Valve OJ has flow rate=0; tunnels lead to valves RP, AM
Valve YN has flow rate=0; tunnels lead to valves SF, JQ
Valve ML has flow rate=0; tunnels lead to valves RI, GG
Valve UJ has flow rate=0; tunnels lead to valves YA, NS
Valve IX has flow rate=0; tunnels lead to valves AN, JQ
Valve JC has flow rate=0; tunnels lead to valves JQ, NJ
Valve TA has flow rate=0; tunnels lead to valves KV, VH
Valve DT has flow rate=16; tunnels lead to valves UU, HU, KA, VH
Valve NR has flow rate=0; tunnels lead to valves HF, CN
Valve YI has flow rate=0; tunnels lead to valves QF, UH
Valve AA has flow rate=0; tunnels lead to valves AM, AN, BN, LA, RJ
Valve BN has flow rate=0; tunnels lead to valves AA, YH
Valve KA has flow rate=0; tunnels lead to valves GG, DT
Valve IL has flow rate=0; tunnels lead to valves GG, XD
Valve CN has flow rate=7; tunnels lead to valves YH, EO, WX, NR, OM
Valve IP has flow rate=0; tunnels lead to valves WX, VT
Valve OM has flow rate=0; tunnels lead to valves CN, JQ
Valve KO has flow rate=0; tunnels lead to valves GQ, HR
Valve LA has flow rate=0; tunnels lead to valves AA, MU
Valve JQ has flow rate=6; tunnels lead to valves IX, JC, PX, YN, OM
Valve IK has flow rate=0; tunnels lead to valves EO, RP
Valve HG has flow rate=0; tunnels lead to valves MH, RP
Valve NS has flow rate=23; tunnels lead to valves CV, BB, UJ

0 comments on commit 633ca01

Please sign in to comment.