From c7b208c659cdcab78395bed877f80b8baedf8516 Mon Sep 17 00:00:00 2001 From: Paul Woitaschek Date: Sat, 14 Dec 2024 08:41:17 +0100 Subject: [PATCH] Visualize day 14. --- 2024/build.gradle.kts | 7 ++ 2024/src/main/kotlin/aoc/year2024/Day14.kt | 88 ++++++++++++++++---- gradle/libs.versions.toml | 2 + library/src/main/kotlin/aoc/library/Input.kt | 11 ++- settings.gradle.kts | 1 + 5 files changed, 92 insertions(+), 17 deletions(-) diff --git a/2024/build.gradle.kts b/2024/build.gradle.kts index 90ebe0f6..9423d6e4 100644 --- a/2024/build.gradle.kts +++ b/2024/build.gradle.kts @@ -1,3 +1,10 @@ plugins { id("aoc") + alias(libs.plugins.jetbrains.compose) + alias(libs.plugins.compose.compiler) +} + +dependencies { + implementation(compose.desktop.common) + implementation(compose.desktop.currentOs) } diff --git a/2024/src/main/kotlin/aoc/year2024/Day14.kt b/2024/src/main/kotlin/aoc/year2024/Day14.kt index ae71c3c3..b972c7ea 100644 --- a/2024/src/main/kotlin/aoc/year2024/Day14.kt +++ b/2024/src/main/kotlin/aoc/year2024/Day14.kt @@ -1,13 +1,35 @@ package aoc.year2024 +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.WindowState +import androidx.compose.ui.window.singleWindowApplication import aoc.library.Point import aoc.library.Puzzle -import aoc.library.print +import aoc.library.aocInput +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlin.String object Day14 : Puzzle(day = 14) { - override fun solvePart1(input: String): Int = solvePart1(input, 101, 103) + private const val DEFAULT_WIDTH = 101 + private const val DEFAULT_HEIGHT = 103 + + override fun solvePart1(input: String): Int = solvePart1(input, DEFAULT_WIDTH, DEFAULT_HEIGHT) fun solvePart1( input: String, @@ -33,19 +55,16 @@ object Day14 : Puzzle(day = 14) { .filterKeys { it != null }.values .reduce(Int::times) - override fun solvePart2(input: String): Int = generateSequence(parse(input)) { robots -> - robots.map { robot -> robot.step(101, 103) } - }.indexOfFirst { robots -> - hasRobotCluster(robots) - .also { hasCluster -> - if (hasCluster) { - val map = robots.groupingBy { it.position }.eachCount() - map.print { - map[it]?.toString() ?: "." - } - } - } + override fun solvePart2(input: String): Int = solvePart2(input, onStep = {}) + + private fun solvePart2( + input: String, + onStep: (List) -> Unit, + ) = generateSequence(parse(input)) { robots -> + robots.map { robot -> robot.step(DEFAULT_WIDTH, DEFAULT_HEIGHT) } } + .onEach { onStep(it) } + .indexOfFirst(::hasRobotCluster) private fun hasRobotCluster(robots: List): Boolean { val positions = robots.map { it.position }.toSet() @@ -75,4 +94,45 @@ object Day14 : Puzzle(day = 14) { ), ) } + + @JvmStatic + fun main(args: Array) { + val tileSize = 8.dp + singleWindowApplication( + title = "Advent of Code in Kotlin", + state = WindowState(width = tileSize * DEFAULT_WIDTH, height = tileSize * DEFAULT_HEIGHT), + ) { + var state by remember { mutableStateOf>(emptySet()) } + LaunchedEffect(Unit) { + val steps = mutableListOf>() + solvePart2( + aocInput(2024, 14), + onStep = { + launch { + steps += it.map { it.position }.toSet() + } + }, + ) + withContext(Dispatchers.Default) {} + steps.forEach { + delay(1) + state = it + } + } + Column { + repeat(DEFAULT_HEIGHT) { y -> + Row { + repeat(DEFAULT_WIDTH) { x -> + val color = if (Point(x, y) in state) { + Color(0xFF228B22) + } else { + Color(0xFF001F3F) + } + Box(Modifier.size(tileSize).background(color)) + } + } + } + } + } + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3c347ade..a646428b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,4 +18,6 @@ kotlin-jvmPlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", versi [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlintGradlePlugin" } +jetbrains-compose = {id="org.jetbrains.compose", version="1.7.1"} diff --git a/library/src/main/kotlin/aoc/library/Input.kt b/library/src/main/kotlin/aoc/library/Input.kt index 5671b695..2a36dd55 100644 --- a/library/src/main/kotlin/aoc/library/Input.kt +++ b/library/src/main/kotlin/aoc/library/Input.kt @@ -11,10 +11,15 @@ fun Puzzle.solvePart1(): Part1 = solvePart1(input() fun Puzzle.solvePart2(): Part2 = solvePart2(input()) -fun Puzzle<*, *>.input(): String = aocInput(day = day) +fun Puzzle<*, *>.input(): String = aocInput( + year = File(System.getProperty("user.dir")).name.toInt(), + day = day, +) -private fun aocInput(day: Int): String { - val year = File(System.getProperty("user.dir")).name +fun aocInput( + year: Int, + day: Int, +): String { val file = File("../inputs/$year/$day.txt") .apply { parentFile.mkdirs() } if (!file.exists() || file.length() == 0L) { diff --git a/settings.gradle.kts b/settings.gradle.kts index ae25fbeb..cbba6cf8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,6 +11,7 @@ pluginManagement { dependencyResolutionManagement { repositories { mavenCentral() + google() } }