From 71fd82743573a528a04be0a2073513cad10cd210 Mon Sep 17 00:00:00 2001 From: gergo Date: Sat, 17 Dec 2022 11:34:15 +0100 Subject: [PATCH] day 16 part one OK, part two WIP --- src/main/kotlin/me/gergo/Aoc16.kt | 121 ++++++++++++++++++++++++++++++ src/main/resources/input16.txt | 63 ++++++++++++++++ 2 files changed, 184 insertions(+) create mode 100644 src/main/kotlin/me/gergo/Aoc16.kt create mode 100644 src/main/resources/input16.txt diff --git a/src/main/kotlin/me/gergo/Aoc16.kt b/src/main/kotlin/me/gergo/Aoc16.kt new file mode 100644 index 0000000..9612380 --- /dev/null +++ b/src/main/kotlin/me/gergo/Aoc16.kt @@ -0,0 +1,121 @@ +package me.gergo + +import java.io.File +import kotlin.math.max + +fun main() { + val valves = File("src/main/resources/input16.txt").readLines() + .mapIndexed(::parseValves) + val valvesByName = valves.associateBy(Valve::name) + val neighbors = valves.associateWith { v -> v.tunnelsTo.map { valvesByName[it]!! } } + + // Part One - dynamic programming, memoization + data class MemoKey(val valve: Valve, val opened: Set, val minute: Int) + + val memo = mutableMapOf() + + fun findMaxPressure(current: Valve, opened: Set, minute: Int): Int { + val key = MemoKey(current, opened, minute) + var max = memo[key] + if (max == null) { + val pressureReleased = opened.sumOf(Valve::rate) + max = pressureReleased + if (minute == 30) 0 + else { + max( + if (current.rate > 0 && !opened.contains(current)) // Stay here and open current valve (if the valve is not damaged) + findMaxPressure(current, opened.plus(current), minute + 1) else 0, + neighbors[current]!!.maxOf { // ...or don't open the valve and move to a neighbor + findMaxPressure(it, opened, minute + 1) + } + ) + } + memo[key] = max + } + return max + } + + // FUK. I spent HOURS wondering why the sample data passes and my puzzle input does not... "You start at valve AA", NOT the first valve :| + val aa = valves.first { it.name == "AA" } + + val result1 = findMaxPressure(aa, emptySet(), 1) + println("Max pressure released : $result1") + + // Part Two + data class MemoKey2( + val me: Valve, + val eli: Valve, + val opened: Long, + val openedRate: Int, + val meVisited: Long, + val eliVisited: Long, + val minute: Int + ) { + fun meCanOpen() = me.rate > 0 && !opened.isBitSet(me.index) + fun eliCanOpen() = eli.rate > 0 && !opened.isBitSet(eli.index) + + fun meNeighbors() = neighbors[me]!! + fun eliNeighbors() = neighbors[eli]!! + + fun meMovesEliOpens(neighbor: Valve) = + MemoKey2(neighbor, eli, opened.setBit(eli.index), openedRate + eli.rate, meVisited.setBit(me.index), eliVisited, minute + 1) + + fun eliMovesMeOpens(neighbor: Valve) = + MemoKey2(me, neighbor, opened.setBit(me.index), openedRate + me.rate, meVisited, eliVisited.setBit(eli.index), minute + 1) + + fun weBothOpen() = + MemoKey2(me, eli, opened.setBit(me.index).setBit(eli.index), openedRate + me.rate + eli.rate, meVisited, eliVisited, minute + 1) + + fun weBothMove(n1: Valve, n2: Valve) = + MemoKey2(n1, n2, opened, openedRate, meVisited.setBit(me.index), eliVisited.setBit(eli.index), minute + 1) + } + + val memo2 = mutableMapOf() + + fun permutations(k: MemoKey2) = sequence { + if (k.meCanOpen() && k.eliCanOpen() && k.me != k.eli) yield(k.weBothOpen()) // Both valves can be opened + if (k.meCanOpen()) + for (n in k.eliNeighbors()) { // My valve can be opened, Eli can move + if (k.meVisited.isBitSet(n.index)) continue // Eli shouldn't go where I went + yield(k.eliMovesMeOpens(n)) + } + if (k.eliCanOpen()) + for (n in k.meNeighbors()) { // Eli's valve can be opened, I can move + if (k.eliVisited.isBitSet(n.index)) continue // I shouldn't go where Eli went + yield(k.meMovesEliOpens(n)) + } + for (mn in k.meNeighbors()) { // We both move + if (k.eliVisited.isBitSet(mn.index)) continue // I shouldn't go where Eli went + for (en in k.eliNeighbors()) { + if (k.meVisited.isBitSet(en.index)) continue // Eli shouldn't go where I went + yield(k.weBothMove(mn, en)) + } + } + } + + fun findMaxPressureWithElephant(key: MemoKey2): Int { + if (key.minute == 26) return key.openedRate // We're done! + + val cachedMax = memo2[key] + if (cachedMax != null) return cachedMax + + val max = key.openedRate + (permutations(key).map(::findMaxPressureWithElephant).maxOrNull() ?: 0) + memo2[key] = max + + if (memo2.size % 1000000 == 0) println("Memo entries: ${memo2.size}") + return max + } + + val result2 = findMaxPressureWithElephant(MemoKey2(aa, aa, 0L, 0, 0L, 0L, 1)) + println("Max pressure released with Eli: $result2") +} + +private fun Long.isBitSet(index: Int): Boolean = this and (1L shl index) != 0L +private fun Long.setBit(index: Int): Long = this or (1L shl index) + +private data class Valve(val index: Int, val name: String, val rate: Int, val tunnelsTo: List) + +private val ValveTunnelFormat = Regex("Valve (\\w+) has flow rate=(\\d+); tunnels? leads? to valves? (.*)") +private fun parseValves(i: Int, line: String): Valve { + val (_, name, rate, tunnels) = ValveTunnelFormat.matchEntire(line)!!.groupValues + return Valve(i, name, rate.toInt(), tunnels.split(", ")) +} diff --git a/src/main/resources/input16.txt b/src/main/resources/input16.txt new file mode 100644 index 0000000..300042b --- /dev/null +++ b/src/main/resources/input16.txt @@ -0,0 +1,63 @@ +Valve OQ has flow rate=17; tunnels lead to valves NB, AK, KL +Valve HP has flow rate=0; tunnels lead to valves ZX, KQ +Valve GO has flow rate=0; tunnels lead to valves HR, GW +Valve PD has flow rate=9; tunnels lead to valves XN, EV, QE, MW +Valve NQ has flow rate=0; tunnels lead to valves HX, ZX +Valve DW has flow rate=0; tunnels lead to valves IR, WE +Valve TN has flow rate=24; tunnels lead to valves KL, EI +Valve JJ has flow rate=0; tunnels lead to valves EV, HR +Valve KH has flow rate=0; tunnels lead to valves ZQ, AA +Valve PH has flow rate=0; tunnels lead to valves FN, QE +Valve FD has flow rate=0; tunnels lead to valves SM, HX +Valve SM has flow rate=7; tunnels lead to valves WW, RZ, FD, HO, KQ +Valve PU has flow rate=0; tunnels lead to valves VL, IR +Valve OM has flow rate=0; tunnels lead to valves CM, AA +Valve KX has flow rate=20; tunnel leads to valve PC +Valve IR has flow rate=3; tunnels lead to valves PU, CM, WW, DW, AF +Valve XG has flow rate=0; tunnels lead to valves RX, OF +Valve QE has flow rate=0; tunnels lead to valves PH, PD +Valve GW has flow rate=0; tunnels lead to valves JQ, GO +Valve HO has flow rate=0; tunnels lead to valves SM, TY +Valve WU has flow rate=0; tunnels lead to valves SG, RZ +Valve MS has flow rate=0; tunnels lead to valves UE, OF +Valve JS has flow rate=0; tunnels lead to valves DO, ZX +Valve YQ has flow rate=0; tunnels lead to valves BC, SG +Valve EJ has flow rate=0; tunnels lead to valves AA, LR +Valve EI has flow rate=0; tunnels lead to valves BV, TN +Valve NC has flow rate=0; tunnels lead to valves TS, BC +Valve AF has flow rate=0; tunnels lead to valves IR, HX +Valve OX has flow rate=0; tunnels lead to valves HR, BV +Valve BF has flow rate=0; tunnels lead to valves JQ, SY +Valve CA has flow rate=0; tunnels lead to valves YD, HX +Valve KQ has flow rate=0; tunnels lead to valves HP, SM +Valve NB has flow rate=0; tunnels lead to valves OQ, OF +Valve SY has flow rate=0; tunnels lead to valves BF, BV +Valve AA has flow rate=0; tunnels lead to valves KH, EJ, OM, TY, DO +Valve BC has flow rate=11; tunnels lead to valves WE, RX, YQ, LR, NC +Valve HR has flow rate=14; tunnels lead to valves OX, GO, JJ +Valve WE has flow rate=0; tunnels lead to valves DW, BC +Valve MW has flow rate=0; tunnels lead to valves JQ, PD +Valve DO has flow rate=0; tunnels lead to valves JS, AA +Valve PC has flow rate=0; tunnels lead to valves AK, KX +Valve YD has flow rate=0; tunnels lead to valves CA, OF +Valve RX has flow rate=0; tunnels lead to valves XG, BC +Valve CM has flow rate=0; tunnels lead to valves IR, OM +Valve HX has flow rate=6; tunnels lead to valves ZQ, NQ, AF, FD, CA +Valve ZQ has flow rate=0; tunnels lead to valves KH, HX +Valve BV has flow rate=21; tunnels lead to valves SY, OX, EI +Valve AK has flow rate=0; tunnels lead to valves PC, OQ +Valve UE has flow rate=0; tunnels lead to valves MS, JQ +Valve LR has flow rate=0; tunnels lead to valves BC, EJ +Valve JQ has flow rate=8; tunnels lead to valves MW, UE, BF, GW +Valve VL has flow rate=0; tunnels lead to valves PU, ZX +Valve EV has flow rate=0; tunnels lead to valves JJ, PD +Valve TS has flow rate=0; tunnels lead to valves NC, ZX +Valve RZ has flow rate=0; tunnels lead to valves SM, WU +Valve OF has flow rate=13; tunnels lead to valves XG, YD, NB, MS, XN +Valve WW has flow rate=0; tunnels lead to valves SM, IR +Valve TY has flow rate=0; tunnels lead to valves HO, AA +Valve XN has flow rate=0; tunnels lead to valves OF, PD +Valve SG has flow rate=15; tunnels lead to valves WU, YQ +Valve FN has flow rate=25; tunnel leads to valve PH +Valve KL has flow rate=0; tunnels lead to valves TN, OQ +Valve ZX has flow rate=5; tunnels lead to valves JS, HP, VL, NQ, TS