Skip to content

Instantly share code, notes, and snippets.

@pchmielowski
Last active December 10, 2022 16:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pchmielowski/24f5bd769c4d91e5ccb2795085e7b024 to your computer and use it in GitHub Desktop.
Save pchmielowski/24f5bd769c4d91e5ccb2795085e7b024 to your computer and use it in GitHub Desktop.
package net.chmielowski.advent.day10
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import net.chmielowski.advent.inputFile
fun main() {
val file = inputFile(object {})
val input = file.readLines()
val states = execute(parse(input)).dropLast(1)
val columns = 40
val rows = 6
application {
var cycle by remember { mutableStateOf(0) }
var sprites by remember { mutableStateOf(emptyList<Sprite>()) }
LaunchedEffect(Unit) {
while (isActive) {
val relativeCycle = cycle % states.size
val state = states[relativeCycle]
delay(10)
if (isSprite(cycle % columns, state)) {
sprites = sprites + Sprite(relativeCycle % columns, relativeCycle / columns, cycle)
}
cycle += 1
}
}
Window(
state = rememberWindowState(size = DpSize(800.dp, 300.dp)),
title = "Cycle: $cycle",
onCloseRequest = ::exitApplication,
) {
Canvas(
modifier = Modifier
.background(Color.Black)
.fillMaxSize()
) {
for (sprite in sprites) {
drawRect(
Color.Green.copy(alpha = alpha(cycle, sprite)),
topLeft = Offset(
x = sprite.x.toFloat() / columns * size.width,
y = sprite.y.toFloat() / rows * size.height,
),
size = Size(
size.width / columns,
size.height / rows,
),
)
}
}
}
}
}
private fun alpha(currentCycle: Int, sprite: Sprite): Float {
val cyclesAgo = currentCycle - sprite.drawnAtCycle
return (1F - cyclesAgo.toFloat() / 40 / 6).coerceIn(0F, 1F)
}
fun parse(input: List<String>) = input.map(::parseInstruction)
private fun parseInstruction(line: String): Instruction {
require(line.isNotEmpty())
return if (line == "noop") {
Instruction.NoOp
} else {
@Suppress("SpellCheckingInspection")
val groupValues = Regex("addx (.+)").find(line)!!.groupValues
Instruction.Add(groupValues[1].toInt())
}
}
fun execute(instructions: List<Instruction>): List<State> {
val states = mutableListOf(State())
for (instruction in instructions) {
val newStates = instruction.execute(states.last())
for (state in newStates) {
states.add(state)
}
}
return states.toList()
}
private fun isSprite(position: Int, state: State) =
position == state.x || position == state.x - 1 || position == state.x + 1
interface Instruction {
object NoOp : Instruction {
override fun execute(state: State) = listOf(
state,
)
}
data class Add(val value: Int) : Instruction {
override fun execute(state: State) = listOf(
state,
state.copy(x = state.x + value),
)
}
fun execute(state: State): List<State>
}
data class State(
val x: Int = 1,
)
data class Sprite(
val x: Int,
val y: Int,
val drawnAtCycle: Int,
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment