Skip to content

Commit 431ca95

Browse files
committed
Solution 2017-07 (Recursive Circus)
1 parent 8f15bcb commit 431ca95

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package de.ronny_h.aoc.year2017.day07
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
5+
fun main() = RecursiveCircus().run("vgzejbd", "1226")
6+
7+
class RecursiveCircus : AdventOfCode<String>(2017, 7) {
8+
override fun part1(input: List<String>): String = input.parsePrograms().findRootProgram()
9+
10+
private fun List<Program>.findRootProgram(): String {
11+
val balancingPrograms = filter { it.balancedPrograms.isNotEmpty() }
12+
val balancedProgramNames = balancingPrograms.flatMap(Program::balancedPrograms)
13+
return balancingPrograms
14+
.map(Program::name)
15+
.filterNot { it in balancedProgramNames }
16+
.single()
17+
}
18+
19+
override fun part2(input: List<String>): String {
20+
val programs = input.parsePrograms()
21+
val root = programs.findRootProgram()
22+
val programsByName = programs.associateBy(Program::name)
23+
24+
data class ProgramWeight(val weight: Int, val fixedWrongWeight: Int = 0)
25+
26+
fun weightOf(programName: String): ProgramWeight {
27+
val program = programsByName.getValue(programName)
28+
if (program.balancedPrograms.isEmpty()) {
29+
return ProgramWeight(program.weight)
30+
}
31+
val subTowerWeights = program.balancedPrograms.map { weightOf(it) }
32+
subTowerWeights.find { it.fixedWrongWeight != 0 }?.let { fixed ->
33+
// we already found the one to fix -> propagate it up the recursive calls
34+
return ProgramWeight(program.weight + subTowerWeights.sumOf { it.weight }, fixed.fixedWrongWeight)
35+
}
36+
37+
val firstSubTowerWeight = subTowerWeights.first().weight
38+
val sameWeightCount = subTowerWeights.count { it.weight == firstSubTowerWeight }
39+
40+
val fixedWrongWeight = when (sameWeightCount) {
41+
subTowerWeights.size -> {
42+
// nothing to rebalance
43+
0
44+
}
45+
46+
1 -> {
47+
// the single program with wrong weight is the first sub-tower's root
48+
val firstSubprogramWeight = programsByName.getValue(program.balancedPrograms.first()).weight
49+
firstSubprogramWeight + subTowerWeights[1].weight - firstSubTowerWeight
50+
}
51+
52+
else -> {
53+
// the single program with wrong weight is one of the others
54+
val differentWeight = subTowerWeights.single { it.weight != firstSubTowerWeight }
55+
val differentWeightIndex = subTowerWeights.indexOfFirst { it.weight != firstSubTowerWeight }
56+
val differentSubprogramWeight =
57+
programsByName.getValue(program.balancedPrograms[differentWeightIndex]).weight
58+
59+
differentSubprogramWeight + firstSubTowerWeight - differentWeight.weight
60+
}
61+
}
62+
return ProgramWeight(program.weight + subTowerWeights.sumOf { it.weight }, fixedWrongWeight)
63+
}
64+
65+
return weightOf(root).fixedWrongWeight.toString()
66+
}
67+
}
68+
69+
data class Program(val name: String, val weight: Int, val balancedPrograms: List<String> = emptyList())
70+
71+
fun List<String>.parsePrograms(): List<Program> = map {
72+
val row = it.split(" -> ")
73+
val (name, weightInBraces) = row.first().split(" ")
74+
val weight = weightInBraces.substring(1..<weightInBraces.lastIndex).toInt()
75+
if (row.size == 1) {
76+
Program(name, weight)
77+
} else {
78+
Program(name, weight, row[1].split(", "))
79+
}
80+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package de.ronny_h.aoc.year2017.day07
2+
3+
import de.ronny_h.aoc.extensions.asList
4+
import io.kotest.core.spec.style.StringSpec
5+
import io.kotest.matchers.shouldBe
6+
7+
class RecursiveCircusTest : StringSpec({
8+
9+
val input = """
10+
pbga (66)
11+
xhth (57)
12+
ebii (61)
13+
havc (66)
14+
ktlj (57)
15+
fwft (72) -> ktlj, cntj, xhth
16+
qoyq (66)
17+
padx (45) -> pbga, havc, qoyq
18+
tknk (41) -> ugml, padx, fwft
19+
jptl (61)
20+
ugml (68) -> gyxo, ebii, jptl
21+
gyxo (61)
22+
cntj (57)
23+
""".asList()
24+
25+
"input can be parsed" {
26+
input.parsePrograms() shouldBe listOf(
27+
Program("pbga", 66),
28+
Program("xhth", 57),
29+
Program("ebii", 61),
30+
Program("havc", 66),
31+
Program("ktlj", 57),
32+
Program("fwft", 72, listOf("ktlj", "cntj", "xhth")),
33+
Program("qoyq", 66),
34+
Program("padx", 45, listOf("pbga", "havc", "qoyq")),
35+
Program("tknk", 41, listOf("ugml", "padx", "fwft")),
36+
Program("jptl", 61),
37+
Program("ugml", 68, listOf("gyxo", "ebii", "jptl")),
38+
Program("gyxo", 61),
39+
Program("cntj", 57),
40+
)
41+
}
42+
43+
"part 1: the name of the bottom program" {
44+
RecursiveCircus().part1(input) shouldBe "tknk"
45+
}
46+
47+
"part 2: the right weight of exactly one program with the wrong weight" {
48+
RecursiveCircus().part2(input) shouldBe "60"
49+
}
50+
})

0 commit comments

Comments
 (0)