Day 14, that wasn't easy to get correct
This commit is contained in:
142
src/main/kotlin/com/basdado/adventofcode/day14/Day14.kt
Normal file
142
src/main/kotlin/com/basdado/adventofcode/day14/Day14.kt
Normal file
@@ -0,0 +1,142 @@
|
||||
package com.basdado.adventofcode.day14
|
||||
|
||||
import com.basdado.adventofcode.day14.Day14.Recipe.Companion.parse
|
||||
import com.basdado.adventofcode.lines
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.min
|
||||
|
||||
const val DAY14_INPUT_PATH = "/day/14/input.txt"
|
||||
|
||||
fun main() {
|
||||
Day14.puzzle1()
|
||||
Day14.puzzle2()
|
||||
}
|
||||
|
||||
object Day14 {
|
||||
|
||||
fun puzzle1() {
|
||||
val recipes = lines(DAY14_INPUT_PATH)
|
||||
.map { parse(it) }
|
||||
.collect(Collectors.toMap<Recipe, String, Recipe>({ it.output.first }, {it}))
|
||||
|
||||
println(calculateOreRequired(recipes))
|
||||
}
|
||||
|
||||
fun puzzle2() {
|
||||
|
||||
val recipes = lines(DAY14_INPUT_PATH)
|
||||
.map { parse(it) }
|
||||
.collect(Collectors.toMap<Recipe, String, Recipe>({ it.output.first }, {it}))
|
||||
|
||||
var oreRequired = 0L
|
||||
var stepSize = 1
|
||||
val maxOre = 1_000_000_000_000L
|
||||
|
||||
var min = 1L
|
||||
var max = 1_000_000_000_000L
|
||||
|
||||
while(min != max - 1) {
|
||||
|
||||
val guess = min + ((max - min) / 2)
|
||||
if (calculateOreRequired(recipes, guess) > maxOre) {
|
||||
max = guess
|
||||
} else {
|
||||
min = guess
|
||||
}
|
||||
// println("Result between $min and $max")
|
||||
|
||||
}
|
||||
println(min)
|
||||
}
|
||||
|
||||
private fun calculateOreRequired(recipes: MutableMap<String, Recipe>, fuelAmount: Long = 1L): Long {
|
||||
|
||||
var res = recipes["FUEL"]!!
|
||||
if (fuelAmount > 1) {
|
||||
res = Recipe(res.input.mapValues { it.value * fuelAmount }, Pair(res.output.first, res.output.second * fuelAmount))
|
||||
// println("Required root recipe: $res")
|
||||
}
|
||||
// keep track of the remaining resources per type due to other reactions
|
||||
val remaining = mutableMapOf<String, Long>()
|
||||
val ingredientsRequiredCache = mutableMapOf<String, Set<String>>(Pair("ORE", emptySet()))
|
||||
// Tactic: we keep simplifying the recipe until we are left with a recipe from ORE to FUEL
|
||||
while (res.input.any { it.key != "ORE" }) {
|
||||
|
||||
// println(res)
|
||||
// println("remaining: $remaining")
|
||||
|
||||
// We replace the ingredient that is not required by any other ingredients in the current recipe
|
||||
val ingredientToReplace = res.input.keys.first {
|
||||
res.input.keys.filter { o -> o != it }
|
||||
.none { o -> calculateIngredientsRequired(o, recipes, ingredientsRequiredCache).contains(it) }
|
||||
}
|
||||
|
||||
// println("Replacing $ingredientToReplace")
|
||||
|
||||
val inputRequiredAmount =
|
||||
res.input[ingredientToReplace] ?: error("Wait? The input $ingredientToReplace can't be found?")
|
||||
val replacementRecipe = recipes[ingredientToReplace]!!
|
||||
val replacementRecipeTimes =
|
||||
ceil(inputRequiredAmount.toDouble() / replacementRecipe.output.second.toDouble()).toLong()
|
||||
// If the recipe will produce more than we need, at it to the remaining
|
||||
remaining[replacementRecipe.output.first] = (remaining[replacementRecipe.output.first] ?: 0L) + (replacementRecipeTimes * replacementRecipe.output.second) - inputRequiredAmount
|
||||
val newInput = res.input.toMutableMap()
|
||||
newInput.remove(ingredientToReplace)
|
||||
replacementRecipe.input.entries.forEach { replacementIngredient ->
|
||||
|
||||
// Be greedy here: if the "remaining" for the replacement ingredient is enough to produce the recipe, we use it
|
||||
// Shit...greedy appears to not be optimal for larger cases...
|
||||
val replacementIngredientRemaining = remaining[replacementIngredient.key] ?: 0
|
||||
val useRemainingTimes =
|
||||
min(replacementRecipeTimes, replacementIngredientRemaining / replacementIngredient.value)
|
||||
if (useRemainingTimes > 0) {
|
||||
remaining[replacementIngredient.key] =
|
||||
remaining[replacementIngredient.key]!! - (useRemainingTimes * replacementIngredient.value)
|
||||
}
|
||||
newInput[replacementIngredient.key] = (newInput[replacementIngredient.key]
|
||||
?: 0) + ((replacementRecipeTimes - useRemainingTimes) * replacementIngredient.value)
|
||||
|
||||
}
|
||||
|
||||
res = Recipe(newInput, res.output)
|
||||
}
|
||||
return res.input["ORE"] ?: error("Errore (hahah ore!)")
|
||||
}
|
||||
|
||||
private fun calculateIngredientsRequired(ingredient: String, recipes: Map<String, Recipe>, cache: MutableMap<String, Set<String>>): Set<String> {
|
||||
|
||||
if (cache.containsKey(ingredient)) return cache[ingredient]!!
|
||||
|
||||
val res = mutableSetOf<String>()
|
||||
val requiredIngredients = recipes[ingredient]?.input ?: return emptySet()
|
||||
res.addAll(requiredIngredients.keys)
|
||||
res.addAll(requiredIngredients.keys.flatMap { calculateIngredientsRequired(it, recipes, cache) })
|
||||
|
||||
cache[ingredient] = res
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
data class Recipe(val input: Map<String, Long>, val output: Pair<String, Long>) {
|
||||
companion object {
|
||||
|
||||
private val ingredientParser = Regex("^(\\d+)\\s+([A-Z]+)$")
|
||||
|
||||
fun parse(str: String): Recipe {
|
||||
val split = str.split("=>").map { it.trim() }
|
||||
val input = split[0].split(",")
|
||||
.map { parseIngredient(it) }
|
||||
.toMap()
|
||||
val outputMatch = ingredientParser.matchEntire(split[1].trim())!!
|
||||
val output = parseIngredient(split[1])
|
||||
return Recipe(input, output)
|
||||
}
|
||||
|
||||
private fun parseIngredient(str: String): Pair<String, Long> {
|
||||
val match = ingredientParser.matchEntire(str.trim())!!
|
||||
return Pair(match.groups[2]!!.value, match.groups[1]!!.value.toLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/main/resources/day/14/ex1.txt
Normal file
6
src/main/resources/day/14/ex1.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
10 ORE => 10 A
|
||||
1 ORE => 1 B
|
||||
7 A, 1 B => 1 C
|
||||
7 A, 1 C => 1 D
|
||||
7 A, 1 D => 1 E
|
||||
7 A, 1 E => 1 FUEL
|
||||
7
src/main/resources/day/14/ex2.txt
Normal file
7
src/main/resources/day/14/ex2.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
9 ORE => 2 A
|
||||
8 ORE => 3 B
|
||||
7 ORE => 5 C
|
||||
3 A, 4 B => 1 AB
|
||||
5 B, 7 C => 1 BC
|
||||
4 C, 1 A => 1 CA
|
||||
2 AB, 3 BC, 4 CA => 1 FUEL
|
||||
9
src/main/resources/day/14/ex3.txt
Normal file
9
src/main/resources/day/14/ex3.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
157 ORE => 5 NZVS
|
||||
165 ORE => 6 DCFZ
|
||||
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL
|
||||
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ
|
||||
179 ORE => 7 PSHF
|
||||
177 ORE => 5 HKGWZ
|
||||
7 DCFZ, 7 PSHF => 2 XJWVT
|
||||
165 ORE => 2 GPVTF
|
||||
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT
|
||||
12
src/main/resources/day/14/ex4.txt
Normal file
12
src/main/resources/day/14/ex4.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG
|
||||
17 NVRVD, 3 JNWZP => 8 VPVL
|
||||
53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL
|
||||
22 VJHF, 37 MNCFX => 5 FWMGM
|
||||
139 ORE => 4 NVRVD
|
||||
144 ORE => 7 JNWZP
|
||||
5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC
|
||||
5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV
|
||||
145 ORE => 6 MNCFX
|
||||
1 NVRVD => 8 CXFTF
|
||||
1 VJHF, 6 MNCFX => 4 RFSQX
|
||||
176 ORE => 6 VJHF
|
||||
17
src/main/resources/day/14/ex5.txt
Normal file
17
src/main/resources/day/14/ex5.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
171 ORE => 8 CNZTR
|
||||
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL
|
||||
114 ORE => 4 BHXH
|
||||
14 VRPVC => 6 BMBT
|
||||
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL
|
||||
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT
|
||||
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW
|
||||
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW
|
||||
5 BMBT => 4 WPTQ
|
||||
189 ORE => 9 KTJDG
|
||||
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP
|
||||
12 VRPVC, 27 CNZTR => 2 XDBXC
|
||||
15 KTJDG, 12 BHXH => 5 XCVML
|
||||
3 BHXH, 2 VRPVC => 7 MZWV
|
||||
121 ORE => 7 VRPVC
|
||||
7 XCVML => 6 RJRHP
|
||||
5 BHXH, 4 VRPVC => 5 LTCX
|
||||
62
src/main/resources/day/14/input.txt
Normal file
62
src/main/resources/day/14/input.txt
Normal file
@@ -0,0 +1,62 @@
|
||||
4 BFNQL => 9 LMCRF
|
||||
2 XGWNS, 7 TCRNC => 5 TPZCH
|
||||
4 RKHMQ, 1 QHRG, 5 JDSNJ => 4 XGWNS
|
||||
6 HWTBC, 4 XGWNS => 6 CWCD
|
||||
1 BKPZH, 2 FLZX => 9 HWFQG
|
||||
1 GDVD, 2 HTSW => 8 CNQW
|
||||
2 RMDG => 9 RKHMQ
|
||||
3 RTLHZ => 3 MSKWT
|
||||
1 QLNHG, 1 RJHCP => 3 GRDJ
|
||||
10 DLSD, 2 SWKHJ, 15 HTSW => 1 TCRNC
|
||||
4 SWKHJ, 24 ZHDSD, 2 DLSD => 3 CPGJ
|
||||
1 SWKHJ => 1 THJHK
|
||||
129 ORE => 8 KLSMQ
|
||||
3 SLNKW, 4 RTLHZ => 4 LPVGC
|
||||
1 SLNKW => 5 RLGFX
|
||||
2 QHRG, 1 SGMK => 8 RJHCP
|
||||
9 RGKCF, 7 QHRG => 6 ZHDSD
|
||||
8 XGWNS, 1 CPGJ => 2 QLNHG
|
||||
2 MQFJF, 7 TBVH, 7 FZXS => 2 WZMRW
|
||||
13 ZHDSD, 11 SLNKW, 18 RJHCP => 2 CZJR
|
||||
1 CNQW, 5 GRDJ, 3 GDVD => 4 FLZX
|
||||
129 ORE => 4 RHSHR
|
||||
2 HWTBC, 2 JDSNJ => 8 QPBHG
|
||||
1 BKPZH, 8 SWKHJ => 6 WSWBV
|
||||
8 RJHCP, 7 FRGJK => 1 GSDT
|
||||
6 QPBHG => 4 BKPZH
|
||||
17 PCRQV, 6 BFNQL, 9 GSDT, 10 MQDHX, 1 ZHDSD, 1 GRDJ, 14 BRGXB, 3 RTLHZ => 8 CFGK
|
||||
8 RMDG => 6 SGMK
|
||||
3 CZJR => 8 RTLHZ
|
||||
3 BFRTV => 7 RGKCF
|
||||
6 FRGJK, 8 CZJR, 4 GRDJ => 4 BRGXB
|
||||
4 VRVGB => 7 PCRQV
|
||||
4 TCRNC, 1 TBVH, 2 FZXS, 1 BQGM, 1 THJHK, 19 RLGFX => 2 CRJTJ
|
||||
5 RDNJK => 6 SWKHJ
|
||||
2 FLVC, 2 SLNKW, 30 HWTBC => 8 DLSD
|
||||
6 TBVH, 3 ZHDSD => 5 BQGM
|
||||
17 RLGFX => 4 SCZQN
|
||||
8 SWKHJ => 6 FZXS
|
||||
9 LZHZ => 3 QDCL
|
||||
2 ZHDSD => 1 RDNJK
|
||||
15 FZXS, 3 TPZCH => 6 MQFJF
|
||||
12 RLGFX, 9 QPBHG, 6 HTSW => 1 BFNQL
|
||||
150 ORE => 9 BFRTV
|
||||
2 BFRTV, 2 KLSMQ => 2 RMDG
|
||||
4 VFLNM, 30 RKHMQ, 4 CRJTJ, 24 CFGK, 21 SCZQN, 4 BMGBG, 9 HWFQG, 34 CWCD, 7 LPVGC, 10 QDCL, 2 WSWBV, 2 WTZX => 1 FUEL
|
||||
6 RHSHR, 3 RGKCF, 1 QHRG => 6 JDSNJ
|
||||
3 MQDHX, 2 XGWNS, 12 GRDJ => 9 LZHZ
|
||||
128 ORE => 6 ZBWLC
|
||||
9 JDSNJ, 7 RMDG => 8 FLVC
|
||||
4 DLSD, 12 CZJR, 3 MSKWT => 4 MQDHX
|
||||
2 BXNX, 4 ZBWLC => 3 QHRG
|
||||
19 LMCRF, 3 JDSNJ => 2 BMGBG
|
||||
1 RJHCP, 26 SGMK => 9 HTSW
|
||||
2 QPBHG => 8 VFLNM
|
||||
2 RGKCF => 9 SLNKW
|
||||
3 LZHZ, 2 GRDJ => 2 TBVH
|
||||
100 ORE => 2 BXNX
|
||||
4 DLSD, 21 JDSNJ => 8 GDVD
|
||||
2 QHRG => 2 HWTBC
|
||||
1 LPVGC, 8 XGWNS => 8 FRGJK
|
||||
9 FZXS => 7 VRVGB
|
||||
7 WZMRW, 1 TBVH, 1 VFLNM, 8 CNQW, 15 LZHZ, 25 PCRQV, 2 BRGXB => 4 WTZX
|
||||
Reference in New Issue
Block a user