GiantTree@feddit.orgtoAdvent Of Code@programming.dev•🐒 - 2024 DAY 22 SOLUTIONS - 🐒English
2·
6 days agoKotlin
I experimented a lot to improve the runtime and now I am happy with my solution. The JVM doesn’t optimize code that quickly :)
I have implemented a few optimizations in regards to transformations so that they use arrays directly (The file with the implementations is here)
Code
class Day22 {
private fun nextSecretNumber(start: Long): Long {
// Modulo 2^24 is the same as "and" with 2^24 - 1
val pruneMask = 16777216L - 1L
// * 64 is the same as shifting left by 6
val mul64 = ((start shl 6) xor start) and pruneMask
// / 32 is the same as shifting right by 5
val div32 = ((mul64 shr 5) xor mul64) and pruneMask
// * 2048 is the same as shifting left by 11
val mul2048 = ((div32 shl 11) xor div32) and pruneMask
return mul2048
}
fun part1(inputFile: String): String {
val secretNumbers = readResourceLines(inputFile)
.map { it.toLong() }
.toLongArray()
repeat(NUMBERS_PER_DAY) {
for (i in secretNumbers.indices) {
secretNumbers[i] = nextSecretNumber(secretNumbers[i])
}
}
return secretNumbers.sum().toString()
}
fun part2(inputFile: String): String {
// There is a different sample input for part 2
val input = if (inputFile.endsWith("sample")) {
readResourceLines(inputFile + "2")
} else {
readResourceLines(inputFile)
}
val buyers = input
.map {
LongArray(NUMBERS_PER_DAY + 1).apply {
this[0] = it.toLong()
for (i in 1..NUMBERS_PER_DAY) {
this[i] = nextSecretNumber(this[i - 1])
}
}
}
// Calculate the prices and price differences for each buyer.
// The pairs are the price (the ones digit) and the key/unique value of each sequence of differences
val differences = buyers
.map { secretNumbers ->
// Get the ones digit
val prices = secretNumbers.mapToIntArray {
it.toInt() % 10
}
// Get the differences between each number
val differenceKeys = prices
.zipWithNext { a, b -> (b - a) }
// Transform the differences to a singular unique value (integer)
.mapWindowed(4) { sequence, from, _ ->
// Bring each byte from -9 to 9 to 0 to 18, multiply by 19^i and sum
// This generates a unique value for each sequence of 4 differences
(sequence[from + 0] + 9) +
(sequence[from + 1] + 9) * 19 +
(sequence[from + 2] + 9) * 361 +
(sequence[from + 3] + 9) * 6859
}
// Drop the first 4 prices, as they are not relevant (initial secret number price and 3 next prices)
prices.dropFromArray(4) to differenceKeys
}
// Cache to hold the value/sum of each sequence of 4 differences
val sequenceCache = IntArray(NUMBER_OF_SEQUENCES)
val seenSequence = BooleanArray(NUMBER_OF_SEQUENCES)
// Go through each sequence of differences
// and get their *first* prices of each sequence.
// Sum them in the cache.
for ((prices, priceDifferences) in differences) {
// Reset the "seen" array
Arrays.fill(seenSequence, false)
for (index in priceDifferences.indices) {
val key = priceDifferences[index]
if (!seenSequence[key]) {
sequenceCache[key] += prices[index]
seenSequence[key] = true
}
}
}
return sequenceCache.max().toString()
}
companion object {
private const val NUMBERS_PER_DAY = 2000
// 19^4, the differences range from -9 to 9 and the sequences are 4 numbers long
private const val NUMBER_OF_SEQUENCES = 19 * 19 * 19 * 19
}
}
Kotlin
A fun and small challenge. First read all locks, transpose their profile and count the
#
s (-1 for the full row). Then do the same for the keys.Lastly find all keys for all locks that do not sum to more than 5 with their teeth:
Code
val lockRegex = Regex("""#{5}(\r?\n[.#]{5}){6}""") val keyRegex = Regex("""([.#]{5}\r?\n){6}#{5}""") fun parseLocksAndKeys(inputFile: String): Pair<List<IntArray>, List<IntArray>> { val input = readResource(inputFile) val locks = lockRegex .findAll(input) .map { it .value .lines() .map { line -> line.toList() } .transpose() .map { line -> line.count { c -> c == '#' } - 1 } .toIntArray() } .toList() val keys = keyRegex .findAll(input) .map { it .value .lines() .map { line -> line.toList() } .transpose() .map { line -> line.count { c -> c == '#' } - 1 } .toIntArray() } .toList() return locks to keys } fun part1(inputFile: String): String { val (locks, keys) = parseLocksAndKeys(inputFile) val matches = locks.map { lock -> keys.filter { key -> for (i in lock.indices) { // Make sure the length of the key and lock do not exceed 5 if (lock[i] + key[i] > 5) { return@filter false } } true } } .flatten() .count() return matches.toString() }
Also on GitHub