Day 12
This commit is contained in:
155
src/main/kotlin/com/basdado/adventofcode/Day12.kt
Normal file
155
src/main/kotlin/com/basdado/adventofcode/Day12.kt
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/main/resources/day/12/example.txt
Normal file
16
src/main/resources/day/12/example.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
initial state: #..#.#..##......###...###
|
||||
|
||||
...## => #
|
||||
..#.. => #
|
||||
.#... => #
|
||||
.#.#. => #
|
||||
.#.## => #
|
||||
.##.. => #
|
||||
.#### => #
|
||||
#.#.# => #
|
||||
#.### => #
|
||||
##.#. => #
|
||||
##.## => #
|
||||
###.. => #
|
||||
###.# => #
|
||||
####. => #
|
||||
34
src/main/resources/day/12/input.txt
Normal file
34
src/main/resources/day/12/input.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
initial state: ..#..####.##.####...#....#######..#.#..#..#.#.#####.######..#.#.#.#..##.###.#....####.#.#....#.#####
|
||||
|
||||
#.##. => .
|
||||
#.#.. => .
|
||||
###.# => .
|
||||
..#.# => .
|
||||
....# => .
|
||||
.#### => .
|
||||
##.## => #
|
||||
###.. => #
|
||||
.###. => #
|
||||
...#. => .
|
||||
..... => .
|
||||
##..# => .
|
||||
.#.#. => #
|
||||
.#.## => #
|
||||
##.#. => .
|
||||
##... => .
|
||||
##### => #
|
||||
#...# => .
|
||||
..##. => .
|
||||
..### => .
|
||||
.#... => #
|
||||
.##.# => .
|
||||
#.... => .
|
||||
.#..# => .
|
||||
.##.. => #
|
||||
...## => #
|
||||
#.### => .
|
||||
#..#. => .
|
||||
..#.. => #
|
||||
#.#.# => #
|
||||
####. => #
|
||||
#..## => .
|
||||
3
src/main/resources/day/12/test.txt
Normal file
3
src/main/resources/day/12/test.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
initial state: #..#
|
||||
...#. => #
|
||||
.#... => #
|
||||
Reference in New Issue
Block a user