generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2015D15.kt
79 lines (67 loc) · 3.24 KB
/
Y2015D15.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package aockt.y2015
import io.github.jadarma.aockt.core.Solution
object Y2015D15 : Solution {
private val inputRegex =
Regex("""(\w+): capacity (-?\d+), durability (-?\d+), flavor (-?\d+), texture (-?\d+), calories (-?\d+)""")
/** Parses a single line of input and returns an [Ingredient] and its properties. */
private fun parseInput(input: String): Ingredient {
val (name, capacity, durability, flavor, texture, calories) = inputRegex.matchEntire(input)!!.destructured
return Ingredient(name, capacity.toInt(), durability.toInt(), flavor.toInt(), texture.toInt(), calories.toInt())
}
/** Cookie making properties and nutritional values of an ingredient. */
private data class Ingredient(
val name: String,
val capacity: Int,
val durability: Int,
val flavor: Int,
val texture: Int,
val calories: Int,
)
/** The required ingredients */
private data class CookieRecipe(val ingredients: Map<Ingredient, Int>) {
val calories by lazy {
ingredients.entries.sumOf { (it, count) -> it.calories * count }
}
val score by lazy {
with(ingredients.entries) {
listOf(
sumOf { (it, count) -> it.capacity * count },
sumOf { (it, count) -> it.durability * count },
sumOf { (it, count) -> it.flavor * count },
sumOf { (it, count) -> it.texture * count },
).map { maxOf(0, it) }.reduce(Int::times)
}
}
}
/** Finds all different way you can use [ingredientCount] to add up to [capacity] units. */
private fun bruteForceMeasurements(ingredientCount: Int, capacity: Int): List<List<Int>> = when (ingredientCount) {
in Int.MIN_VALUE..0 -> throw IllegalArgumentException("What would you want to cook with no ingredients?")
1 -> listOf(listOf(capacity))
else -> (0..capacity).flatMap { measure ->
bruteForceMeasurements(ingredientCount - 1, capacity - measure).map { listOf(measure) + it }
}
}
/**
* With the available [ingredients] and using exactly [capacity] units of measurement in total, finds the right
* combination that maximises the cookie score. Optionally adds [calorieRequirement] on the final recipe.
* Returns the perfect [CookieRecipe] or `null` if it's impossible to bake one to the given specifications.
*/
private fun findPerfectRecipe(
ingredients: List<Ingredient>,
capacity: Int = 100,
calorieRequirement: Int? = null,
): CookieRecipe? =
bruteForceMeasurements(ingredients.size, capacity)
.map { ingredients.zip(it).toMap() }
.map(::CookieRecipe)
.filter { calorieRequirement == null || it.calories == calorieRequirement }
.maxByOrNull { it.score }
override fun partOne(input: String): Any {
val ingredients = input.lineSequence().map(this::parseInput).toList()
return findPerfectRecipe(ingredients)!!.score
}
override fun partTwo(input: String): Any {
val ingredients = input.lineSequence().map(this::parseInput).toList()
return findPerfectRecipe(ingredients, calorieRequirement = 500)!!.score
}
}