Day 7: Bridge Repair
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
You are viewing a single thread.
View all comments 1 point
*
Kotlin
I finally got around to doing day 7. I try the brute force method (takes several seconds), but I’m particularly proud of my sequence generator for operation permutations.
The Collection
method is in the file Utils.kt
, which can be found in my repo.
Solution
import kotlin.collections.any
import kotlin.math.pow
fun main() {
fun part1(input: List<String>): Long {
val operations = setOf(CalibrationOperation.Plus, CalibrationOperation.Multiply)
return generalizedSolution(input, operations)
}
fun part2(input: List<String>): Long {
val operations = setOf(CalibrationOperation.Plus, CalibrationOperation.Multiply, CalibrationOperation.Concat)
return generalizedSolution(input, operations)
}
val testInput = readInput("Day07_test")
check(part1(testInput) == 3749L)
check(part2(testInput) == 11387L)
val input = readInput("Day07")
part1(input).println()
part2(input).println()
}
fun parseInputDay7(input: List<String>) = input.map {
val calibrationResultAndInput = it.split(':')
calibrationResultAndInput[0].toLong() to calibrationResultAndInput[1].split(' ').filter { it != "" }.map { it.toLong() }
}
fun generalizedSolution(input: List<String>, operations: Set<CalibrationOperation>): Long {
val parsedInput = parseInputDay7(input)
val operationsPermutations = CalibrationOperation.operationPermutationSequence(*operations.toTypedArray()).take(calculatePermutationsNeeded(parsedInput, operations)).toList()
return sumOfPossibleCalibrationEquations(parsedInput, operationsPermutations)
}
fun calculatePermutationsNeeded(parsedInput: List<Pair<Long, List<Long>>>, operations: Set<CalibrationOperation>): Int {
val highestNumberOfOperations = parsedInput.maxOf { it.second.size - 1 }
return (1..highestNumberOfOperations).sumOf { operations.size.toDouble().pow(it).toInt() }
}
fun sumOfPossibleCalibrationEquations(parsedInput: List<Pair<Long, List<Long>>>, operationPermutationCollection: Collection<OperationPermutation>): Long {
val permutationsGrouped = operationPermutationCollection.groupBy { it.size }
return parsedInput.sumOf { (equationResult, equationInput) ->
if (permutationsGrouped[equationInput.size - 1]!!.any { operations ->
equationResult == equationInput.drop(1)
.foldIndexed(equationInput[0]) { index, acc, lng -> operations[index](acc, lng) }
}) equationResult else 0
}
}
typealias OperationPermutation = List<CalibrationOperation>
sealed class CalibrationOperation(val operation: (Long, Long) -> Long) {
operator fun invoke(a: Long, b: Long) = operation(a, b)
object Plus : CalibrationOperation({ a: Long, b: Long -> a + b })
object Multiply : CalibrationOperation({ a: Long, b: Long -> a * b })
object Concat : CalibrationOperation({ a: Long, b: Long -> "$a$b".toLong() })
companion object {
fun operationPermutationSequence(vararg operations: CalibrationOperation) = sequence<OperationPermutation> {
val cache = mutableListOf<OperationPermutation>()
val calculateCacheRange = { currentLength: Int ->
val sectionSize = operations.size.toDouble().pow(currentLength - 1).toInt()
val sectionStart = (1 until currentLength - 1).sumOf { operations.size.toDouble().pow(it).toInt() }
sectionStart..(sectionStart + sectionSize - 1)
}
// Populate the cache with initial values for permutation length 1.
operations.forEach { operation -> yield(listOf(operation).also { cache.add(it) }) }
var currentLength = 2
var offset = 0
var cacheRange = calculateCacheRange(currentLength)
var rotatingOperations = operations.toList()
yieldAll(
generateSequence {
if (cacheRange.count() == offset) {
rotatingOperations = rotatingOperations.rotated(1)
if (rotatingOperations.first() == operations.first()) {
currentLength++
}
offset = 0
cacheRange = calculateCacheRange(currentLength)
}
val cacheSlice = cache.slice(cacheRange)
return@generateSequence (cacheSlice[offset] + rotatingOperations.first()).also {
cache += it
offset++
}
}
)
}
}
}