Coverage Summary for Class: Day12p1Kt (day12p1)
Class |
Method, %
|
Line, %
|
Day12p1Kt |
100%
(14/14)
|
100%
(89/89)
|
Day12p1Kt$toConditionRecordRow1Text$1 |
100%
(1/1)
|
100%
(1/1)
|
Day12p1Kt$toIntList$1 |
100%
(1/1)
|
100%
(1/1)
|
Day12p1Kt$WhenMappings |
Total |
100%
(16/16)
|
100%
(91/91)
|
package day12p1
import day12p1.HotSpring.*
import day12p1.HotSpring.Companion.toHotSpring
fun String.sumOfSolutions(): Long = lineSequence().sumOf { it.toConditionRecordRow().countSolutions() }
fun List<HotSpring>.calcContiguousGroupOfDamagedSprings(): List<Int> {
val contGroupOfDamagedSprings = mutableListOf<Int>()
var currentSize = 0
forEach {
when (it) {
broken -> currentSize++
else -> {
require(it == operational)
if (currentSize > 0) {
contGroupOfDamagedSprings.add(currentSize)
currentSize = 0
}
}
}
}
if (currentSize > 0) contGroupOfDamagedSprings.add(currentSize)
return contGroupOfDamagedSprings
}
fun String.toConditionRecordRow(): ConditionRecordRow {
val (crRow1, contGroupOfDamagedSprings) = split(" ")
return ConditionRecordRow(
crRow1.toHotSpringList(),
contGroupOfDamagedSprings.toIntList()
)
}
data class ConditionRecordRow(
val hotSprings: List<HotSpring>,
val contGroupOfDamagedSprings: List<Int>
) {
val numberOfUnknowns = hotSprings.count { it == unknown }
private fun solutions(): Sequence<List<HotSpring>> {
val solutions = mutableListOf<List<HotSpring>>()
processNext(hotSpringsToProcess = hotSprings, brokenGroupsToProcess = contGroupOfDamagedSprings) {
solutions.add(it)
}
return solutions.asSequence()
}
fun countSolutions(): Long {
var count = 0L
processNext(hotSpringsToProcess = hotSprings, brokenGroupsToProcess = contGroupOfDamagedSprings) {
count++
}
return count
}
fun solutionsAsText(): Sequence<String> = solutions().map { it.toConditionRecordRow1Text() }
}
private fun processNext(
result: MutableList<HotSpring> = mutableListOf(),
hotSpringsToProcess: List<HotSpring>,
remainingBrokensInCurrentGroup: Int? = null,
brokenGroupsToProcess: List<Int>,
solutionConsumer: (List<HotSpring>) -> Unit
) {
if (hotSpringsToProcess.isEmpty()) {
processEnd(result, hotSpringsToProcess, remainingBrokensInCurrentGroup, brokenGroupsToProcess, solutionConsumer)
return
}
val next = hotSpringsToProcess.first()
val remaining = hotSpringsToProcess.subList(1, hotSpringsToProcess.size)
when (next) {
operational -> processOperational(
result,
remaining,
remainingBrokensInCurrentGroup,
brokenGroupsToProcess,
solutionConsumer
)
broken -> processBroken(
result,
remaining,
remainingBrokensInCurrentGroup,
brokenGroupsToProcess,
solutionConsumer
)
unknown -> processUnknown(
result,
remaining,
remainingBrokensInCurrentGroup,
brokenGroupsToProcess,
solutionConsumer
)
}
}
private fun processOperational(
result: MutableList<HotSpring>,
hotSpringsToProcess: List<HotSpring>,
remainingBrokensInGroup: Int?,
brokenGroupsToProcess: List<Int>,
solutionConsumer: (List<HotSpring>) -> Unit
) {
if (remainingBrokensInGroup.isCurrentGroupNotEmpty()) {
return
}
addCurrentAndProcessNext(
operational,
result,
hotSpringsToProcess,
null,
brokenGroupsToProcess,
solutionConsumer
)
}
fun processBroken(
result: MutableList<HotSpring>,
hotSpringsToProcess: List<HotSpring>,
remainingBrokensInGroup: Int?,
brokenGroupsToProcess: List<Int>,
solutionConsumer: (List<HotSpring>) -> Unit
) {
val nextRemainingBrokensInGroup: Int
val nextBrokenGroupsToProcess: List<Int>
if (remainingBrokensInGroup != null) {
nextRemainingBrokensInGroup = remainingBrokensInGroup - 1
nextBrokenGroupsToProcess = brokenGroupsToProcess
} else if (brokenGroupsToProcess.isEmpty()) {
return
} else {
nextRemainingBrokensInGroup = brokenGroupsToProcess.first() - 1
nextBrokenGroupsToProcess = brokenGroupsToProcess.subList(1, brokenGroupsToProcess.size)
}
if (nextRemainingBrokensInGroup < 0) return
addCurrentAndProcessNext(
broken,
result,
hotSpringsToProcess,
nextRemainingBrokensInGroup,
nextBrokenGroupsToProcess,
solutionConsumer
)
}
fun processUnknown(
result: MutableList<HotSpring>,
hotSpringsToProcess: List<HotSpring>,
remainingBrokensInCurrentGroup: Int?,
brokenGroupsToProcess: List<Int>,
solutionConsumer: (List<HotSpring>) -> Unit
) {
processOperational(
result,
hotSpringsToProcess,
remainingBrokensInCurrentGroup,
brokenGroupsToProcess,
solutionConsumer
)
processBroken(
result,
hotSpringsToProcess,
remainingBrokensInCurrentGroup,
brokenGroupsToProcess,
solutionConsumer
)
}
private fun processEnd(
result: MutableList<HotSpring>,
hotSpringsToProcess: List<HotSpring>,
remainingBrokensInCurrentGroup: Int?,
brokenGroupsToProcess: List<Int>,
solutionConsumer: (List<HotSpring>) -> Unit
) {
require(hotSpringsToProcess.isEmpty())
if (remainingBrokensInCurrentGroup.isCurrentGroupNotEmpty() || brokenGroupsToProcess.isNotEmpty()) return
solutionConsumer(result.toList())
}
private fun Int?.isCurrentGroupNotEmpty(): Boolean = this != null && this > 0
private fun addCurrentAndProcessNext(
current: HotSpring,
result: MutableList<HotSpring>,
hotSpringsToProcess: List<HotSpring>,
remainingBrokensInGroup: Int?,
brokenGroupsToProcess: List<Int>,
solutionConsumer: (List<HotSpring>) -> Unit
) {
result.add(current)
processNext(result, hotSpringsToProcess, remainingBrokensInGroup, brokenGroupsToProcess, solutionConsumer)
result.removeLast()
}
private val intRegex = """(\d+)""".toRegex()
fun String.toIntList(): List<Int> = intRegex.findAll(this).map { it.value.toInt() }.toList()
fun List<HotSpring>.toConditionRecordRow1Text(): String = joinToString("") { it.crChar.toString() }
fun String.toHotSpringList(): List<HotSpring> = map { it.toHotSpring() }
enum class HotSpring(val crChar: Char) {
operational('.'),
broken('#'),
unknown('?');
companion object {
private val hotSpringsByCrChar = entries.associateBy { it.crChar }
fun Char.toHotSpring() = hotSpringsByCrChar.getValue(this)
}
}