This commit is contained in:
2018-12-13 02:25:43 +01:00
parent 1730dfe955
commit 1d0080083b
4 changed files with 208 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
package com.basdado.adventofcode
import java.util.*
import java.util.stream.Collectors
import kotlin.math.max
import kotlin.math.min
const val INPUT = "/day/12/input.txt"
fun main() {
val day = Day12()
day.puzzle1()
day.puzzle2()
}
class Day12 {
fun puzzle1() {
val initialState = parseInitialState()
val plantPatterns = parsePlantPatterns()
var lastPlantState = getPlantStateAfterGeneration(initialState, plantPatterns, 20)
println(lastPlantState.calcPlantSum())
}
fun puzzle2() {
val initialState = parseInitialState()
val plantPatterns = parsePlantPatterns()
val lastPlantState = getPlantStateAfterGeneration(initialState, plantPatterns, 50000000000L)
println(lastPlantState.calcPlantSum())
}
private fun getPlantStateAfterGeneration(
initialState: BooleanArray,
plantPatterns: BooleanArray,
generationCount: Long
): PlantState {
var lastPlantState = PlantState(0, initialState)
// println("0: $lastPlantState")
for (i in 1L..generationCount) {
val newPlantState = PlantState(lastPlantState.firstPlantIndex - 2, BooleanArray(lastPlantState.state.size + 4))
for (plantIndex in (newPlantState.firstPlantIndex) until (newPlantState.firstPlantIndex + newPlantState.state.size)) {
val patternIndex = lastPlantState.getPatternIndex(plantIndex)
newPlantState.set(plantIndex, plantPatterns[patternIndex])
}
val trimmedNewState = newPlantState.trimmed()
if (lastPlantState.equalsShifted(trimmedNewState)) {
// So the only changes from now on is shifting the plants a little bit every generation
val shift = trimmedNewState.firstPlantIndex - lastPlantState.firstPlantIndex
val totalShift = shift * (generationCount - i)
return PlantState(trimmedNewState.firstPlantIndex + totalShift, trimmedNewState.state)
}
lastPlantState = trimmedNewState
// println("$i: $newPlantState")
}
return lastPlantState
}
private fun parsePlantPatterns(): BooleanArray {
val plantPatternRegex = Regex("""([.#]{5}) => ([.#])""")
val plantPatternIndices = lines(INPUT)
.map { plantPatternRegex.matchEntire(it) }
.filter { it != null }
.filter { it!!.groupValues[2] == "#" }
.map { plantPatternToIndex(it!!.groupValues[1]) }
.collect(Collectors.toList())
return BooleanArray(32) { plantPatternIndices.contains(it) }
}
private fun parseInitialState(): BooleanArray {
val initialStateRegex = Regex("""initial state: ([.#]+)""")
return plantsToBooleanArray(lines(INPUT).filter { initialStateRegex.matches(it) }.findFirst()
.map { initialStateRegex.matchEntire(it) }.get().groupValues[1])
}
companion object {
@JvmStatic
fun plantsToBooleanArray(plants: String): BooleanArray {
return plants.chars().mapToObj { it == '#'.toInt() }.toArray { Array(it) { false } }.toBooleanArray()
}
@JvmStatic
fun plantPatternToIndex(plantPattern: String): Int {
assert(plantPattern.length == 5)
var res = 0
(0 until plantPattern.length).forEach { i ->
if (plantPattern[i] == '#') {
res = res or (1 shl i)
}
}
return res
}
}
class PlantState(val firstPlantIndex: Long, val state: BooleanArray) {
val lastPlantIndex = firstPlantIndex + state.size
fun getPatternIndex(plantIndex: Long): Int {
var res = 0
for (i in 0 until 5) {
if (get(plantIndex + i - 2L)) {
res = res or (1 shl i)
}
}
return res
}
private fun stateIndex(plantIndex: Long): Int = (plantIndex - firstPlantIndex).toInt()
private fun plantIndex(stateIndex: Int): Long = firstPlantIndex + stateIndex
fun set(plantIndex: Long, value: Boolean) {
state[stateIndex(plantIndex)] = value
}
fun get(plantIndex: Long): Boolean {
val stateIndex = stateIndex(plantIndex)
return stateIndex >= 0 && stateIndex < state.size && state[stateIndex]
}
override fun toString(): String {
return state.joinToString("") { if (it) "#" else "." }
}
fun calcPlantSum(): Long = (firstPlantIndex until (firstPlantIndex + state.size)).filter { get(it) }.sum()
fun trimmed(): PlantState {
val firstPlantStateIndex = state.indexOfFirst { it }
val lastPlantStateIndex = state.indexOfLast { it }
if (firstPlantStateIndex == 0 && lastPlantStateIndex == state.size - 1) {
return this
}
return PlantState(plantIndex(firstPlantStateIndex), state.copyOfRange(firstPlantStateIndex, lastPlantStateIndex + 1))
}
override fun equals(other: Any?): Boolean {
return other is PlantState &&
(min(firstPlantIndex, other.firstPlantIndex)..(max(lastPlantIndex, other.lastPlantIndex))).all { get(it) == other.get(it) }
}
fun equalsShifted(other: PlantState): Boolean {
val thisTrimmed = this.trimmed()
val otherTrimmed = other.trimmed()
return Arrays.equals(thisTrimmed.state, otherTrimmed.state)
}
}
}

View File

@@ -0,0 +1,16 @@
initial state: #..#.#..##......###...###
...## => #
..#.. => #
.#... => #
.#.#. => #
.#.## => #
.##.. => #
.#### => #
#.#.# => #
#.### => #
##.#. => #
##.## => #
###.. => #
###.# => #
####. => #

View File

@@ -0,0 +1,34 @@
initial state: ..#..####.##.####...#....#######..#.#..#..#.#.#####.######..#.#.#.#..##.###.#....####.#.#....#.#####
#.##. => .
#.#.. => .
###.# => .
..#.# => .
....# => .
.#### => .
##.## => #
###.. => #
.###. => #
...#. => .
..... => .
##..# => .
.#.#. => #
.#.## => #
##.#. => .
##... => .
##### => #
#...# => .
..##. => .
..### => .
.#... => #
.##.# => .
#.... => .
.#..# => .
.##.. => #
...## => #
#.### => .
#..#. => .
..#.. => #
#.#.# => #
####. => #
#..## => .

View File

@@ -0,0 +1,3 @@
initial state: #..#
...#. => #
.#... => #