-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDay12.kt
79 lines (67 loc) · 2.84 KB
/
Day12.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
/*
* Copyright (c) 2018 by Todd Ginsberg
*/
/**
* Advent of Code 2018, Day 12 - Subterranean Sustainability
*
* Problem Description: http://adventofcode.com/2018/day/12
* Blog Post/Commentary: https://todd.ginsberg.com/post/advent-of-code/2018/day12/
*/
package com.ginsberg.advent2018
class Day12(rawInput: List<String>) {
private val rules: Set<String> = parseRules(rawInput)
private val initialState: String = rawInput.first().substring(15)
fun solvePart1(): Long =
mutatePlants().drop(19).first().second
fun solvePart2(targetGeneration: Long = 50_000_000_000): Long {
var previousDiff = 0L
var previousSize = 0L
var generationNumber = 0
// Go through the sequence until we find one that grows the same one as its previous generation
mutatePlants().dropWhile { thisGen ->
val thisDiff = thisGen.second - previousSize // Our diff to last generation
if (thisDiff != previousDiff) {
// Still changing
previousDiff = thisDiff
previousSize = thisGen.second
generationNumber += 1
true
} else {
// We've found it, stop dropping.
false
}
}.first() // Consume first because sequences are lazy and it won't start otherwise.
return previousSize + (previousDiff * (targetGeneration - generationNumber))
}
private fun mutatePlants(state: String = initialState): Sequence<Pair<String, Long>> = sequence {
var zeroIndex = 0
var currentState = state
while (true) {
// Make sure we have something to match to the left of our first real center point.
while (!currentState.startsWith(".....")) {
currentState = ".$currentState"
zeroIndex++
}
// Make sure we have something to match to the right of our last real center point.
while (!currentState.endsWith(".....")) {
currentState = "$currentState."
}
currentState = currentState
.toList()
.windowed(5, 1)
.map { it.joinToString(separator = "") }
.map { if (it in rules) '#' else '.' }
.joinToString(separator = "")
zeroIndex -= 2 // Because there are two positions to the left of the first real center and were not evaluated
yield(Pair(currentState, currentState.sumIndexesFrom(zeroIndex)))
}
}
private fun String.sumIndexesFrom(zero: Int): Long =
this.mapIndexed { idx, c -> if (c == '#') idx.toLong() - zero else 0 }.sum()
private fun parseRules(input: List<String>): Set<String> =
input
.drop(2)
.filter { it.endsWith("#") }
.map { it.take(5) }
.toSet()
}