Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1 +1 @@
java temurin-21.0.2+13.0.LTS
java 21
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile

plugins {
kotlin("jvm")
id("com.gradleup.shadow")
Expand All @@ -21,6 +19,7 @@ repositories {
dependencies {
implementation(kotlin("stdlib"))
implementation(KotlinX.coroutines.core)
implementation(KotlinX.coroutines.reactive)
implementation("info.picocli:picocli:_")
implementation("io.klogging:slf4j-klogging:_")
testImplementation(kotlin("test"))
Expand Down
8 changes: 8 additions & 0 deletions src/main/kotlin/cz/glubo/adventofcode/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ import cz.glubo.adventofcode.y2024.day22.y2024day22part1
import cz.glubo.adventofcode.y2024.day22.y2024day22part2
import cz.glubo.adventofcode.y2024.day23.y2024day23part1
import cz.glubo.adventofcode.y2024.day23.y2024day23part2
import cz.glubo.adventofcode.y2024.day24.y2024day24part1
import cz.glubo.adventofcode.y2024.day24.y2024day24part2
import cz.glubo.adventofcode.y2024.day25.y2024day25part1
import cz.glubo.adventofcode.y2024.day25.y2024day25part2
import cz.glubo.adventofcode.y2024.day3.y2024day3part1
import cz.glubo.adventofcode.y2024.day3.y2024day3part2
import cz.glubo.adventofcode.y2024.day4.y2024day4part1
Expand Down Expand Up @@ -287,6 +291,10 @@ fun main(args: Array<String>) {
"2024day22p2" to InputToLongCommand { y2024day22part2(it) },
"2024day23p1" to InputToLongCommand { y2024day23part1(it) },
"2024day23p2" to InputToStringCommand { y2024day23part2(it) },
"2024day24p1" to InputToLongCommand { y2024day24part1(it) },
"2024day24p2" to InputToStringCommand { y2024day24part2(it, 4) },
"2024day25p1" to InputToLongCommand { y2024day25part1(it) },
"2024day25p2" to InputToLongCommand { y2024day25part2(it) },
)

val cmd = CommandLine(MyHelpCommand())
Expand Down
32 changes: 32 additions & 0 deletions src/main/kotlin/cz/glubo/adventofcode/utils/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,35 @@ fun <E> Set<E>.allSubsets(): Sequence<Set<E>> =
yield(setOf(first) + it)
}
}

fun <E> Set<E>.allOrders(): Sequence<List<E>> =
sequence {
val list = this@allOrders.toList()
if (list.isEmpty()) {
yield(emptyList())
} else {
for (i in list.indices) {
val otherSet =
list
.filterIndexed { it, _ -> it != i }
.toSet()
otherSet.allOrders().forEach { item ->
yield(item + list [i])
}
}
}
}

fun Long.getBit(n: Int) = this.shr(n).and(1)

fun Long.bitDistance(other: Long): Int {
var a = this
var b = other
var result = 0
do {
result += a.and(1).xor(b.and(1)).toInt()
a = a.shr(1)
b = b.shr(1)
} while (a > 0 || b > 0)
return result
}
233 changes: 233 additions & 0 deletions src/main/kotlin/cz/glubo/adventofcode/y2024/day24/Day24.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package cz.glubo.adventofcode.y2024.day24

import cz.glubo.adventofcode.utils.bitDistance
import cz.glubo.adventofcode.utils.input.Input
import io.klogging.noCoLogger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import kotlin.experimental.and
import kotlin.experimental.or
import kotlin.experimental.xor

val logger = noCoLogger({}.javaClass.toString())

suspend fun y2024day24part1(input: Input): Long {
logger.info("year 2024 day 24 part 1")

val lines = input.lineFlow().toList()
val stateLines = lines.takeWhile { it.isNotBlank() }
var gates = parseGates(lines.takeLastWhile { it.isNotBlank() })

val stateMap = parseStateMap(stateLines)

val result =
compute(
stateMap,
gates,
emptyMap(),
)
return result
}

enum class Operation(
val compute: (Short, Short) -> Short,
) {
AND({ a, b -> a.and(b) }),
OR({ a, b -> a.or(b) }),
XOR({ a, b -> a.xor(b) }),
}

data class Gate(
val inputA: String,
val inputB: String,
val operation: Operation,
val target: String,
)

fun computeState(
inputStateMap: Map<String, Short>,
gates: List<Gate>,
swapMap: Map<String, String>,
): Map<String, Short> {
val stateMap = inputStateMap.toMutableMap()
var operationsLeft = gates
do {
var removed = false

operationsLeft =
operationsLeft.filterIndexed { index, op ->
if (!stateMap.contains(op.inputA) || !stateMap.contains(op.inputB)) {
return@filterIndexed true
}
removed = true
val result =
op.operation.compute(
stateMap[op.inputA]!!,
stateMap[op.inputB]!!,
)

val target = swapMap.getOrDefault(op.target, op.target)
stateMap[target] = result

false
}
} while (removed)
return stateMap
}

fun compute(
inputStateMap: Map<String, Short>,
gates: List<Gate>,
swapMap: Map<String, String>,
): Long {
val stateMap = computeState(inputStateMap, gates, swapMap)

return stateMap.variable("z")
}

fun Map<String, Short>.variable(prefix: String) =
this
.filter { (key, value) ->
key.startsWith(prefix)
}.entries
.sortedByDescending { it.key }
.fold(0L) { acc, it -> acc.shl(1) + it.value }

suspend fun y2024day24part2(
input: Input,
swaps: Int,
): String {
logger.info("year 2024 day 24 part 2")
val lines = input.lineFlow().toList()
val stateLines = lines.takeWhile { it.isNotBlank() }
var gates = parseGates(lines.takeLastWhile { it.isNotBlank() })

val stateMap = parseStateMap(stateLines)

val x = stateMap.variable("x")
val y = stateMap.variable("y")
val wantedResult = x + y

val badResult = compute(stateMap, gates, emptyMap())
val badBitmap = badResult.xor(wantedResult)

val wantedS = wantedResult.toString(2)
logger.info { "wantedResult: $wantedS" }
logger.info { "badbadResult: ${badResult.toString(2)}" }
logger.info { "badResultBmap ${badBitmap.toString(2).padStart(wantedS.length, '0')}" }
val a =
findSwaps(
wantedResult,
stateMap,
gates,
emptyMap(),
4,
)

return a!!.joinToString(",")
}

suspend fun findSwaps(
wantedResult: Long,
stateMap: MutableMap<String, Short>,
gates: List<Gate>,
swapMap: Map<String, String>,
i: Int,
): List<String>? {
val names =
gates
.map { it.target }
.filter { !swapMap.containsKey(it) }

val swapErrorList =
(0..<names.size)
.flatMap { i ->
(i + 1..<names.size).map { j ->
val newSwapMap =
mapOf(
names[i] to names[j],
names[j] to names[i],
) + swapMap
val result = compute(stateMap, gates, newSwapMap)

newSwapMap to result.bitDistance(wantedResult)
}
}.sortedBy { it.second }
.toMutableList()

do {
runBlocking(Dispatchers.IO) {
val chunk = (1..3).map { _ -> swapErrorList.removeFirst() }
val results =
chunk.map {
async {
if (it.second == 0) {
logger.info { "found ${it.first}" }
return@async it.first.keys.sorted()
}
if (i > 1) {
findSwaps(
wantedResult,
stateMap,
gates,
it.first,
i - 1,
)?.let { s -> return@async s }
}
null
}
}
results.awaitAll().filterNotNull()
}.let {
if (it.isNotEmpty()) return it.first()
}
} while (swapErrorList.isNotEmpty())

// swapErrorList.forEach {
// if (it.second == 0) {
// logger.info { "found ${it.first}" }
// return it.first.keys.sorted()
// }
// if (i > 1) {
// findSwaps(
// wantedResult,
// stateMap,
// gates,
// it.first,
// i - 1,
// )?.let { s -> return s }
// }
// null
// }
return null
}

private fun parseStateMap(stateLines: List<String>): MutableMap<String, Short> {
val stateMap = mutableMapOf<String, Short>()

stateLines.forEach { line ->
val (name, value) = line.split(": ")
stateMap[name] = value.toShort()
}
return stateMap
}

private fun parseGates(lines: List<String>) =
lines
.map { line ->
val (a, operand, b, _, target) = line.split(" ")
Gate(
a,
b,
when (operand) {
"AND" -> Operation.AND
"OR" -> Operation.OR
"XOR" -> Operation.XOR
else -> error("unknown operand $operand")
},
target,
)
}
70 changes: 70 additions & 0 deletions src/main/kotlin/cz/glubo/adventofcode/y2024/day25/Day25.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package cz.glubo.adventofcode.y2024.day25

import cz.glubo.adventofcode.utils.input.Input
import io.klogging.noCoLogger
import kotlinx.coroutines.flow.toList

val logger = noCoLogger({}.javaClass.toString())

fun parseItemAndPush(
buf: MutableList<String>,
items: MutableList<List<Int>>,
): Int {
val item =
buf.first().indices.map { i ->
buf.sumOf {
val height = if (it[i] == '#') 1 else 0
height
} - 1
}

items.addLast(item)

return buf.size - 1
}

fun parseLockOrKeyAndPushIt(
buf: MutableList<String>,
keys: MutableList<List<Int>>,
locks: MutableList<List<Int>>,
): Int =
when {
buf.isEmpty() -> 0
buf.first().all { it == '#' } -> parseItemAndPush(buf, keys)
buf.last().all { it == '#' } -> parseItemAndPush(buf, locks)
else -> error("Not a key or a lock: $buf")
}

suspend fun y2024day25part1(input: Input): Long {
logger.info("year 2024 day 25 part 1")
val keys = mutableListOf<List<Int>>()
val locks = mutableListOf<List<Int>>()
var buf = mutableListOf<String>()
var height: Int = 0
input
.lineFlow()
.toList()
.forEach { line ->
if (line.isNotBlank()) {
buf += line
} else {
height = parseLockOrKeyAndPushIt(buf, keys, locks)
buf.clear()
}
}
parseLockOrKeyAndPushIt(buf, keys, locks)

return keys
.sumOf { key ->
locks.count { lock ->
key.indices.all { i ->
(key[i] + lock[i]) < height
}
}
}.toLong()
}

suspend fun y2024day25part2(input: Input): Long {
logger.info("year 2024 day 25 part 2")
return 0
}
Loading
Loading