Skip to content

Commit

Permalink
Day 1: Historian Hysteria
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 1, 2024
1 parent 692d4fb commit 4f25eeb
Show file tree
Hide file tree
Showing 30 changed files with 767 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# generated by Gradle Wrapper
kt/gradlew linguist-generated
kt/gradlew.bat linguist-generated
39 changes: 39 additions & 0 deletions .github/workflows/kt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Kotlin CI

on:
push:
branches: [ main ]
paths: [ kt/** ]
pull_request:
branches: [ main ]
paths: [ kt/** ]

workflow_dispatch:

jobs:
get-inputs:
uses: ephemient/aoc2024/.github/workflows/get-inputs.yml@main
secrets:
SESSION: ${{ secrets.SESSION }}

build:
needs: [ get-inputs ]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: inputs
path: inputs
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- uses: gradle/actions/setup-gradle@v4
- run: ./gradlew check
working-directory: kt
- run: ./gradlew :aoc2024-exe:runJvm
working-directory: kt
env:
AOC2024_DATADIR: ${{ github.workspace }}/inputs
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

Development occurs in language-specific directories:

|[Haskell](hs) ![Haskell CI](https://github.com/ephemient/aoc2024/workflows/Haskell%20CI/badge.svg)|
|--:|
|[Day1.hs](hs/src/Day1.hs)|
|[Haskell](hs) ![Haskell CI](https://github.com/ephemient/aoc2024/workflows/Haskell%20CI/badge.svg)|[Kotlin](kt) ![Kotlin CI](https://github.com/ephemient/aoc2024/workflows/Kotlin%20CI/badge.svg)|
|--:|--:|
|[Day1.hs](hs/src/Day1.hs)|[Day1.kt](kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day1.kt)|
1 change: 1 addition & 0 deletions kt/.envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export AOC2024_DATADIR=$(realpath ..)
6 changes: 6 additions & 0 deletions kt/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.gradle/
.kotlin/
.idea/
build/
local.properties
*~
22 changes: 22 additions & 0 deletions kt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# [Advent of Code 2024](https://adventofcode.com/2024)
### my answers in [Kotlin](https://www.kotlinlang.org/) ![Kotlin CI](https://github.com/ephemient/aoc2024/workflows/Kotlin%20CI/badge.svg)

This project builds with [Gradle](https://gradle.org/).

Run the test suite:

```sh
./gradlew :aoc2024-lib:allTests
```

Print solutions for the inputs provided in local data files:

```sh
./gradlew :aoc2024-exe:jvmRun
```

Run all checks, including [Detekt](https://detekt.github.io/) static code analysis:

```sh
./gradlew check
```
27 changes: 27 additions & 0 deletions kt/aoc2024-exe/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import io.gitlab.arturbosch.detekt.Detekt

plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.detekt)
}

kotlin {
jvm {
mainRun {
mainClass = "com.github.ephemient.aoc2024.exe.Main"
}
}

sourceSets {
commonMain {
dependencies {
implementation(projects.aoc2024Lib)
implementation(libs.kotlinx.coroutines)
}
}
}
}

dependencies {
detektPlugins(libs.bundles.detekt.plugins)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.ephemient.aoc2024.exe

import com.github.ephemient.aoc2024.days

internal suspend fun mainImpl(args: Array<out String>) {
for (day in days) {
if ((args.isNotEmpty() || day.skipByDefault) && day.name !in args) continue
println("Day ${day.name}")
for (part in day.solver(getDayInput(day.day))) println(part())
println()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.github.ephemient.aoc2024.exe

internal expect fun getDayInput(day: Int): String
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.github.ephemient.aoc2024.exe

import java.io.File

internal actual fun getDayInput(day: Int): String =
File(System.getenv("AOC2024_DATADIR")?.ifEmpty { null } ?: ".", "day$day.txt").readText()
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@file:JvmName("Main")

package com.github.ephemient.aoc2024.exe

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

@Suppress("InjectDispatcher")
suspend fun main(vararg args: String): Unit = withContext(Dispatchers.Default) {
mainImpl(args)
}
39 changes: 39 additions & 0 deletions kt/aoc2024-lib/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
plugins {
alias(libs.plugins.kotlin.multiplatform)
alias(libs.plugins.detekt)
}

kotlin {
jvm()

sourceSets {
commonMain {
dependencies {
implementation(libs.kotlinx.coroutines)
}
}

commonTest {
dependencies {
implementation(kotlin("test"))
implementation(libs.kotlinx.coroutines.test)
}
}

jvmTest {
dependencies {
implementation(kotlin("test-junit5"))
implementation(libs.junit.jupiter.api)
runtimeOnly(libs.junit.jupiter.engine)
}
}
}
}

dependencies {
detektPlugins(libs.bundles.detekt.plugins)
}

tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.github.ephemient.aoc2024

internal class CommonPriorityQueue<E : Any>(private val comparator: Comparator<in E>) : PriorityQueue<E> {
private val storage = mutableListOf<E>()

override fun isEmpty(): Boolean = storage.isEmpty()

override fun add(element: E): Boolean {
storage.add(element)
var i = storage.lastIndex
while (i > 0) {
val j = (i - 1) / 2
val a = storage[j]
val b = storage[i]
if (comparator.compare(a, b) <= 0) break
storage[i] = a
storage[j] = b
i = j
}
return true
}

@Suppress("NestedBlockDepth")
@Throws(NoSuchElementException::class)
override fun remove(): E {
val first = storage.first()
val last = storage.removeLast()
if (storage.isNotEmpty()) {
storage[0] = last
var i = 0
while (2 * i + 2 < storage.size) {
val j = if (comparator.compare(storage[2 * i + 1], storage[2 * i + 2]) < 0) 2 * i + 1 else 2 * i + 2
if (comparator.compare(storage[i], storage[j]) <= 0) break
storage[i] = storage[j].also { storage[j] = storage[i] }
i = j
}
if (2 * i + 1 == storage.lastIndex && comparator.compare(storage[i], storage.last()) > 0) {
storage[i] = storage.last().also { storage[storage.lastIndex] = storage[i] }
}
}
return first
}

override fun iterator(): Iterator<E> = storage.iterator()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.github.ephemient.aoc2024

import kotlin.math.abs

class Day1(input: String) {
private val left: List<Int>
private val right: List<Int>

init {
val left = mutableListOf<Int>()
val right = mutableListOf<Int>()
for (line in input.lineSequence()) {
val parts = line.trim().split(splitter, limit = 2)
val x = parts.getOrNull(0)?.toIntOrNull() ?: continue
val y = parts.getOrNull(1)?.toIntOrNull() ?: continue
left.add(x)
right.add(y)
}
this.left = left.toList()
this.right = right.toList()
}

fun part1() = left.sorted().zip(right.sorted(), Int::minus).sumOf(::abs)

fun part2(): Int {
val right = right.groupingBy { it }.eachCount()
return left.sumOf { it * right.getOrElse(it) { 0 } }
}

companion object {
private val splitter = """\s+""".toRegex()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.github.ephemient.aoc2024

val days: List<Day> = listOf(
Day(1, ::Day1, Day1::part1, Day1::part2),
)

data class Day(
val day: Int,
val parts: Int,
val solver: (String) -> List<suspend () -> Any?>,
val name: String = day.toString(),
val skipByDefault: Boolean = false,
)

fun <T> Day(
day: Int,
create: (String) -> T,
vararg parts: suspend (T) -> Any?,
name: String = day.toString(),
skipByDefault: Boolean = false,
): Day = Day(
day = day,
parts = parts.size,
solver = { with(create(it)) { parts.map { suspend { it.invoke(this) } } } },
name = name,
skipByDefault = skipByDefault,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.ephemient.aoc2024

data class IntPair(val first: Int, val second: Int) {
override fun toString(): String = "($first, $second)"
}

infix fun Int.to(other: Int): IntPair = IntPair(this, other)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.github.ephemient.aoc2024

fun gcd(x: Long, y: Long): Long {
var a = x
var b = y
while (b != 0L) a = b.also { b = a.mod(b) }
return a
}

fun lcm(x: Long, y: Long): Long = x / gcd(x, y) * y
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.github.ephemient.aoc2024

import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.fold
import kotlinx.coroutines.launch

suspend fun <T> Iterable<T>.parSum(block: (T) -> Long): Long = channelFlow {
for (value in this@parSum) {
launch {
send(block(value))
}
}
}.fold(0, Long::plus)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.github.ephemient.aoc2024

interface PriorityQueue<E : Any> : Iterable<E> {
fun isEmpty(): Boolean

fun add(element: E): Boolean

@Throws(NoSuchElementException::class)
fun remove(): E
}

expect fun <E : Any> PriorityQueue(comparator: Comparator<E>): PriorityQueue<E>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.ephemient.aoc2024

fun Iterable<String>.transpose(): List<String> = buildList {
val strings = this@transpose.filterTo(mutableListOf()) { it.isNotEmpty() }
var i = 0
while (strings.isNotEmpty()) {
add(buildString(strings.size) { for (string in strings) append(string[i]) })
i++
strings.removeAll { it.length == i }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.github.ephemient.aoc2024

import kotlin.test.Test
import kotlin.test.assertEquals

class Day1Test {
@Test
fun part1() {
assertEquals(11, Day1(example).part1())
}

@Test
fun part2() {
assertEquals(31, Day1(example).part2())
}

companion object {
private val example =
"""
|3 4
|4 3
|2 5
|1 3
|3 9
|3 3
|""".trimMargin()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.github.ephemient.aoc2024

internal class JvmPriorityQueue<E : Any>(comparator: Comparator<E>) :
PriorityQueue<E>,
java.util.PriorityQueue<E>(comparator)

actual fun <E : Any> PriorityQueue(comparator: Comparator<E>): PriorityQueue<E> =
JvmPriorityQueue(comparator)
5 changes: 5 additions & 0 deletions kt/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
plugins {
alias(libs.plugins.kotlin.multiplatform) apply false
}

group = "com.github.ephemient.aoc2024"
Loading

0 comments on commit 4f25eeb

Please sign in to comment.