Skip to content

Commit fd6a650

Browse files
committed
Solution 2018-12, part 2 (Subterranean Sustainability)
1 parent 183753e commit fd6a650

File tree

2 files changed

+83
-30
lines changed

2 files changed

+83
-30
lines changed

src/main/kotlin/de/ronny_h/aoc/year2018/day12/SubterraneanSustainability.kt

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,66 +3,119 @@ package de.ronny_h.aoc.year2018.day12
33
import de.ronny_h.aoc.AdventOfCode
44
import de.ronny_h.aoc.extensions.collections.split
55

6-
fun main() = SubterraneanSustainability().run(3798, 0)
6+
private val verbose = false
77

8-
class SubterraneanSustainability : AdventOfCode<Int>(2018, 12) {
9-
override fun part1(input: List<String>): Int {
8+
fun main() = SubterraneanSustainability().run(3798, 3900000002212)
9+
10+
class SubterraneanSustainability : AdventOfCode<Long>(2018, 12) {
11+
override fun part1(input: List<String>): Long {
1012
val pots = GameOfPlants(input, 20)
1113
pots.simulateGenerations()
1214
return pots.sumOfPlantContainingPotNumbers()
1315
}
1416

15-
override fun part2(input: List<String>): Int {
16-
return 0
17+
override fun part2(input: List<String>): Long {
18+
val generations = 50000000000
19+
val pots = GameOfPlants(input, generations)
20+
val (generationCount, shifted) = pots.simulateGenerations()
21+
return pots.sumOfPlantContainingPotNumbers(generations - generationCount, shifted)
1722
}
1823
}
1924

20-
class GameOfPlants(input: List<String>, val generations: Int) {
25+
class GameOfPlants(input: List<String>, private val generations: Long) {
2126
private val plant = '#'
2227
private val noPlant = '.'
23-
private val initialState = input.first().substringAfter("initial state: ")
28+
private val initialState = input.first().substringAfter("initial state: ").toList()
2429

2530
// a rule has the form of: LLCRR => N
2631
private val plantProducingPatterns = input.split()[1]
2732
.map { it.split(" => ") }
2833
.filter { it[1] == "$plant" }
29-
.map { it.first() }
34+
.map { it.first().toList() }
3035
.toSet()
3136
private val patternLength = 5
3237
private val patternCenter = 2
3338

3439
// per generation the plants grow max 2 pots wider in each direction
35-
private val offset = generations * 2
36-
private val padding = "$noPlant".repeat(offset)
37-
38-
var currentGeneration = padding + initialState + padding
39-
private set
40+
private val paddingLength = 40
41+
private var offset = 0
42+
private val padding = List(paddingLength) { noPlant }
4043

41-
fun nextGeneration(): String {
42-
val nextGen = currentGeneration.toMutableList()
44+
private var currentGeneration = initialState.toMutableList()
45+
private var nextGeneration = currentGeneration.toMutableList()
4346

44-
for (index in 0..nextGen.lastIndex - patternLength) {
45-
nextGen[index + patternCenter] =
46-
if (currentGeneration.substring(index, index + patternLength) in plantProducingPatterns) {
47+
fun nextGeneration(): List<Char> {
48+
var firstPlantIndex = currentGeneration.indexOf(plant)
49+
if (firstPlantIndex < patternLength) {
50+
firstPlantIndex += addPaddingAtFront()
51+
}
52+
if (currentGeneration.lastIndexOf(plant) > currentGeneration.lastIndex - patternLength) {
53+
addPaddingAtEnd()
54+
}
55+
for (index in firstPlantIndex - patternLength..nextGeneration.lastIndex - patternLength) {
56+
nextGeneration[index + patternCenter] =
57+
if (currentGeneration.subList(index, index + patternLength) in plantProducingPatterns) {
4758
plant
4859
} else {
4960
noPlant
5061
}
5162
}
52-
currentGeneration = nextGen.joinToString("")
63+
swapGenerations()
5364
return currentGeneration
5465
}
5566

56-
fun sumOfPlantContainingPotNumbers(): Int =
57-
currentGeneration.foldIndexed(0) { i, acc, pot ->
58-
if (pot == plant) acc + i - offset else acc
67+
private fun swapGenerations() {
68+
val tmp = currentGeneration
69+
currentGeneration = nextGeneration
70+
nextGeneration = tmp
71+
}
72+
73+
private fun addPaddingAtFront(): Int {
74+
if (verbose) println("adding padding at the beginning")
75+
offset += paddingLength
76+
currentGeneration = (padding + currentGeneration).toMutableList()
77+
nextGeneration = currentGeneration.toMutableList()
78+
return paddingLength
79+
}
80+
81+
private fun addPaddingAtEnd() {
82+
if (verbose) println("adding padding at the end")
83+
currentGeneration = (currentGeneration + padding).toMutableList()
84+
nextGeneration = currentGeneration.toMutableList()
85+
}
86+
87+
fun sumOfPlantContainingPotNumbers(remainingGenerations: Long = 0L, shiftPerGeneration: Int = 0): Long {
88+
val correction = remainingGenerations * shiftPerGeneration - offset
89+
return currentGeneration.foldIndexed(0L) { i, acc, pot ->
90+
if (pot == plant) acc + i + correction else acc
5991
}
92+
}
6093

61-
fun simulateGenerations() {
62-
println("generation 0:$currentGeneration")
63-
repeat(generations) {
94+
// returns: a pair of
95+
// - the number of the generation at which the configuration of plants does not change anymore and
96+
// - the number of positions the pattern is shifted compared to the previous generation
97+
fun simulateGenerations(): Pair<Long, Int> {
98+
if (verbose) println("generation 0:${currentGeneration.joinToString("")}")
99+
var generationCount = 1L
100+
var lastPlantCount = currentGeneration.count { it == plant }
101+
while (generationCount <= generations) {
64102
nextGeneration()
65-
println("generation ${"%2d".format(it + 1)}: $currentGeneration")
103+
if (generationCount < 202 || generationCount % 10000000 == 0L) {
104+
if (verbose) println("generation ${"%2d".format(generationCount)}: ${currentGeneration.joinToString("")}")
105+
}
106+
val thisPlantCount = currentGeneration.count { it == plant }
107+
if (thisPlantCount == lastPlantCount) {
108+
if (currentGeneration.allPlantsContainingSubList() == nextGeneration.allPlantsContainingSubList()) {
109+
if (verbose) println("generation $generationCount: plants configuration did not change")
110+
return generationCount to currentGeneration.indexOf(plant) - nextGeneration.indexOf(plant)
111+
}
112+
}
113+
generationCount++
114+
lastPlantCount = thisPlantCount
66115
}
116+
return generationCount to 0
67117
}
118+
119+
private fun MutableList<Char>.allPlantsContainingSubList(): MutableList<Char> =
120+
subList(indexOf(plant), lastIndexOf(plant) + 1)
68121
}

src/test/kotlin/de/ronny_h/aoc/year2018/day12/SubterraneanSustainabilityTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ class SubterraneanSustainabilityTest : StringSpec({
2727
""".asList()
2828

2929
"the first generation of the sample input" {
30-
GameOfPlants(input, 20).nextGeneration() shouldContain "...#...#....#.....#..#..#..#..........."
30+
GameOfPlants(input, 20).nextGeneration()
31+
.joinToString("") shouldContain "...#...#....#.....#..#..#..#..........."
3132
}
3233

3334
"part 1: the sum of the numbers of plant-containing pots after the 20th generation" {
3435
SubterraneanSustainability().part1(input) shouldBe 325
3536
}
3637

37-
"part 2" {
38-
val input = listOf("")
39-
SubterraneanSustainability().part2(input) shouldBe 0
38+
"part 2 the sum of the numbers of plant-containing pots after the 50000000000th generation" {
39+
SubterraneanSustainability().part2(input) shouldBe 999999999374L
4040
}
4141
})

0 commit comments

Comments
 (0)