Compare commits
2 Commits
b470715f12
...
dc4160ca73
| Author | SHA1 | Date | |
|---|---|---|---|
| dc4160ca73 | |||
| 7e31325d16 |
@ -19,9 +19,24 @@ object Libs {
|
|||||||
const val processor = "org.openjdk.jmh:jmh-generator-annprocess:1.26"
|
const val processor = "org.openjdk.jmh:jmh-generator-annprocess:1.26"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Micronaut {
|
||||||
|
private const val version = "2.2.0"
|
||||||
|
const val inject = "io.micronaut:micronaut-inject:$version"
|
||||||
|
const val core = "io.micronaut:micronaut-core:$version"
|
||||||
|
const val kotlin = "io.micronaut.kotlin:micronaut-kotlin-extension-functions:$version"
|
||||||
|
const val processor = "io.micronaut:micronaut-inject-java:$version"
|
||||||
|
}
|
||||||
|
|
||||||
|
object Slf4J {
|
||||||
|
const val api = "org.slf4j:slf4j-api:1.7.25"
|
||||||
|
const val logback = "ch.qos.logback:logback-classic:1.2.3"
|
||||||
|
const val kotlin = "io.github.microutils:kotlin-logging-jvm:2.0.3"
|
||||||
|
}
|
||||||
|
|
||||||
object Tests {
|
object Tests {
|
||||||
const val assertJ = "org.assertj:assertj-core:3.18.1"
|
const val assertJ = "org.assertj:assertj-core:3.18.1"
|
||||||
const val junit = "org.junit.jupiter:junit-jupiter:5.7.0"
|
const val junit = "org.junit.jupiter:junit-jupiter:5.7.0"
|
||||||
|
const val jimfs = "com.google.jimfs:jimfs:1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,13 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("kotlin-convention")
|
id("kotlin-convention")
|
||||||
|
kotlin("kapt")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(project(":utils"))
|
implementation(project(":utils"))
|
||||||
|
|
||||||
|
implementation(Libs.Micronaut.core)
|
||||||
|
implementation(Libs.Micronaut.inject)
|
||||||
|
implementation(Libs.Micronaut.kotlin)
|
||||||
|
kapt(Libs.Micronaut.processor)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,27 @@
|
|||||||
package be.vandewalleh.aoc.days
|
package be.vandewalleh.aoc.days
|
||||||
|
|
||||||
import be.vandewalleh.aoc.utils.Resources
|
import be.vandewalleh.aoc.utils.input.Day
|
||||||
|
import be.vandewalleh.aoc.utils.input.Input
|
||||||
|
import be.vandewalleh.aoc.utils.input.Lines
|
||||||
|
import be.vandewalleh.aoc.utils.input.createDay
|
||||||
|
|
||||||
object Day01 {
|
@Day(1)
|
||||||
|
class Day01(@Lines input: Input<IntArray>) {
|
||||||
|
private val items = input.value
|
||||||
|
|
||||||
fun part1(list: List<Int>): Int? {
|
fun part1(): Int? {
|
||||||
list.forEach { a ->
|
items.forEach { a ->
|
||||||
list.forEach { b ->
|
items.forEach { b ->
|
||||||
if (a + b == 2020) return a * b
|
if (a + b == 2020) return a * b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun part2(list: List<Int>): Int? {
|
fun part2(): Int? {
|
||||||
list.forEach { a ->
|
items.forEach { a ->
|
||||||
list.forEach { b ->
|
items.forEach { b ->
|
||||||
list.forEach { c ->
|
items.forEach { c ->
|
||||||
if (a + b + c == 2020) return a * b * c
|
if (a + b + c == 2020) return a * b * c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,8 +31,7 @@ object Day01 {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun main() {
|
fun main() = with(createDay<Day01>()) {
|
||||||
val input = Resources.ints("day01.txt").toList()
|
println(part1())
|
||||||
println(Day01.part1(input))
|
println(part2())
|
||||||
println(Day01.part2(input))
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,22 @@
|
|||||||
package be.vandewalleh.aoc.days
|
package be.vandewalleh.aoc.days
|
||||||
|
|
||||||
import be.vandewalleh.aoc.utils.Resources
|
import io.micronaut.context.BeanContext
|
||||||
|
import io.micronaut.context.getBean
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class Day01Test {
|
class Day01Test {
|
||||||
|
|
||||||
private val input = Resources.ints("day01.txt").toList()
|
private val day = BeanContext.run().getBean<Day01>()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `part1 result`() {
|
fun `part1 result`() {
|
||||||
assertThat(Day01.part1(input)).isEqualTo(32064)
|
assertThat(day.part1()).isEqualTo(32064)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `part2 result`() {
|
fun `part2 result`() {
|
||||||
assertThat(Day01.part2(input)).isEqualTo(193598720)
|
assertThat(day.part2()).isEqualTo(193598720)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,18 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("kotlin-convention")
|
id("kotlin-convention")
|
||||||
|
kotlin("kapt")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(Libs.Micronaut.core)
|
||||||
|
implementation(Libs.Micronaut.inject)
|
||||||
|
implementation(Libs.Micronaut.kotlin)
|
||||||
|
kapt(Libs.Micronaut.processor)
|
||||||
|
|
||||||
|
implementation(Libs.Slf4J.api)
|
||||||
|
implementation(Libs.Slf4J.kotlin)
|
||||||
|
runtimeOnly(Libs.Slf4J.logback)
|
||||||
|
|
||||||
|
testImplementation(Libs.Tests.jimfs)
|
||||||
|
kaptTest(Libs.Micronaut.processor)
|
||||||
}
|
}
|
||||||
|
|||||||
1
utils/src/main/kotlin/be/vandewalleh/aoc/utils/Index.kt
Normal file
1
utils/src/main/kotlin/be/vandewalleh/aoc/utils/Index.kt
Normal file
@ -0,0 +1 @@
|
|||||||
|
package be.vandewalleh.aoc.utils
|
||||||
@ -1,28 +0,0 @@
|
|||||||
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
|
||||||
|
|
||||||
package be.vandewalleh.aoc.utils
|
|
||||||
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Path
|
|
||||||
import kotlin.streams.asSequence
|
|
||||||
|
|
||||||
object Resources {
|
|
||||||
private fun path(name: String): Path {
|
|
||||||
val url = if (name.startsWith('/')) javaClass.getResource(name)
|
|
||||||
else javaClass.getResource("/$name")
|
|
||||||
return Path.of(url.toURI())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Path.lines() = Files.lines(this)
|
|
||||||
.asSequence()
|
|
||||||
.filter { it.isNotBlank() }
|
|
||||||
.map { it.trim() }
|
|
||||||
|
|
||||||
private fun Path.text() = Files.readString(this).trim()
|
|
||||||
|
|
||||||
fun text(name: String) = path(name).text()
|
|
||||||
fun lines(name: String) = path(name).lines()
|
|
||||||
fun ints(name: String) = lines(name).map { it.toInt() }
|
|
||||||
fun csv(name: String) = path(name).text().splitToSequence(",")
|
|
||||||
fun csvLong(name: String) = csv(name).map { it.toLong() }
|
|
||||||
}
|
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
@file:Suppress("unused")
|
||||||
|
|
||||||
|
package be.vandewalleh.aoc.utils.input
|
||||||
|
|
||||||
|
import io.micronaut.context.annotation.Prototype
|
||||||
|
import io.micronaut.core.annotation.Introspected
|
||||||
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
|
@Prototype
|
||||||
|
@Qualifier
|
||||||
|
@Introspected
|
||||||
|
@Target(AnnotationTarget.CLASS)
|
||||||
|
annotation class Day(val day: Int)
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Prototype
|
||||||
|
annotation class DayInput
|
||||||
|
|
||||||
|
@DayInput
|
||||||
|
annotation class Csv
|
||||||
|
|
||||||
|
@DayInput
|
||||||
|
annotation class Text
|
||||||
|
|
||||||
|
@DayInput
|
||||||
|
annotation class Lines
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
package be.vandewalleh.aoc.utils.input
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper class so that micronaut is not confused with an other injectable
|
||||||
|
* container type when using an iterable / array
|
||||||
|
*/
|
||||||
|
data class Input<T>(val value: T)
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
package be.vandewalleh.aoc.utils.input
|
||||||
|
|
||||||
|
import io.micronaut.context.annotation.Factory
|
||||||
|
import io.micronaut.inject.InjectionPoint
|
||||||
|
import kotlin.io.path.ExperimentalPathApi
|
||||||
|
import kotlin.io.path.readLines
|
||||||
|
import kotlin.io.path.readText
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load challenge inputs and convert them to the appropriate format
|
||||||
|
*
|
||||||
|
* contains horrible hacks while waiting for generic support in bean injection
|
||||||
|
* @see [micronaut-2775](https://github.com/micronaut-projects/micronaut-core/issues/2775)
|
||||||
|
*/
|
||||||
|
@Factory
|
||||||
|
@ExperimentalPathApi
|
||||||
|
class InputFactory(private val resourceLoader: ResourceLoader) {
|
||||||
|
|
||||||
|
@Csv
|
||||||
|
fun csv(injectionPoint: InjectionPoint<*>): Input<*> =
|
||||||
|
when (val param = injectionPoint.typeNameOfAnnotation<Csv>()) {
|
||||||
|
INT_ARRAY -> injectionPoint
|
||||||
|
.read()
|
||||||
|
.split(",")
|
||||||
|
.map { it.toInt() }
|
||||||
|
.toIntArray()
|
||||||
|
.wrap()
|
||||||
|
LONG_ARRAY -> injectionPoint
|
||||||
|
.read()
|
||||||
|
.split(",")
|
||||||
|
.map { it.toLong() }
|
||||||
|
.toLongArray()
|
||||||
|
.wrap()
|
||||||
|
else -> error("Unsupported type $param")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Lines
|
||||||
|
fun lines(injectionPoint: InjectionPoint<*>): Input<*> =
|
||||||
|
when (val param = injectionPoint.typeNameOfAnnotation<Lines>()) {
|
||||||
|
INT_ARRAY -> injectionPoint
|
||||||
|
.lines()
|
||||||
|
.map { it.toInt() }
|
||||||
|
.toList()
|
||||||
|
.toIntArray()
|
||||||
|
.wrap()
|
||||||
|
LONG_ARRAY -> injectionPoint
|
||||||
|
.lines()
|
||||||
|
.map { it.toLong() }
|
||||||
|
.toList()
|
||||||
|
.toLongArray()
|
||||||
|
.wrap()
|
||||||
|
STRING_LIST -> injectionPoint
|
||||||
|
.lines()
|
||||||
|
.toList()
|
||||||
|
.wrap()
|
||||||
|
else -> error("Unsupported type $param")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Text
|
||||||
|
fun text(injectionPoint: InjectionPoint<*>): Input<String> =
|
||||||
|
injectionPoint.read().wrap()
|
||||||
|
|
||||||
|
|
||||||
|
private fun <T> T.wrap() = Input(this)
|
||||||
|
|
||||||
|
private inline fun <reified T : Annotation> InjectionPoint<*>.typeNameOfAnnotation() = declaringBean
|
||||||
|
.constructor
|
||||||
|
.arguments
|
||||||
|
.find { it.hasAnnotation<T>() }
|
||||||
|
?.typeName
|
||||||
|
?.removePrefix("be.vandewalleh.aoc.utils.input.Input<")
|
||||||
|
?.removeSuffix(">")
|
||||||
|
?: error("??")
|
||||||
|
|
||||||
|
private fun InjectionPoint<*>.path() = resourceLoader.ofDay(getDay(this))
|
||||||
|
private fun InjectionPoint<*>.read() = path().readText().trim()
|
||||||
|
private fun InjectionPoint<*>.lines() = path().readLines()
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
|
||||||
|
private fun getDay(injectionPoint: InjectionPoint<*>): Int {
|
||||||
|
val dayAnnotation = injectionPoint
|
||||||
|
.declaringBean
|
||||||
|
.findAnnotation<Day>()
|
||||||
|
|
||||||
|
if (dayAnnotation.isEmpty)
|
||||||
|
error("@DayInput cannot only be used on classes annotated with ${Day::class.qualifiedName}")
|
||||||
|
|
||||||
|
return dayAnnotation.get().intValue("day").asInt
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val INT_ARRAY = "int[]"
|
||||||
|
private const val LONG_ARRAY = "long[]"
|
||||||
|
private const val STRING_LIST = "java.util.List<java.lang.String>"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
package be.vandewalleh.aoc.utils.input
|
||||||
|
|
||||||
|
import io.micronaut.context.BeanContext
|
||||||
|
import io.micronaut.context.getBean
|
||||||
|
import io.micronaut.core.annotation.AnnotationMetadataDelegate
|
||||||
|
import io.micronaut.core.annotation.AnnotationMetadataProvider
|
||||||
|
import io.micronaut.core.annotation.AnnotationValue
|
||||||
|
import io.micronaut.core.beans.BeanIntrospection
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
internal inline fun <reified T> getIntrospection(): BeanIntrospection<T> =
|
||||||
|
BeanIntrospection.getIntrospection(T::class.java)
|
||||||
|
|
||||||
|
internal inline fun <reified T : Annotation> AnnotationMetadataDelegate.getAnnotation(): AnnotationValue<T>? =
|
||||||
|
getAnnotation(T::class.java)
|
||||||
|
|
||||||
|
internal inline fun <reified T : Annotation> AnnotationMetadataProvider.findAnnotation(): Optional<AnnotationValue<T>> =
|
||||||
|
findAnnotation(T::class.java)
|
||||||
|
|
||||||
|
internal inline fun <reified T : Annotation> AnnotationMetadataProvider.hasAnnotation(): Boolean =
|
||||||
|
findAnnotation(T::class.java).isPresent
|
||||||
|
|
||||||
|
inline fun <reified T> createDay() = BeanContext.run().getBean<T>()
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
package be.vandewalleh.aoc.utils.input
|
||||||
|
|
||||||
|
import java.nio.file.Path
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
interface ResourceLoader {
|
||||||
|
fun ofDay(day: Int): Path
|
||||||
|
}
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class ResourceLoaderImpl : ResourceLoader {
|
||||||
|
override fun ofDay(day: Int): Path {
|
||||||
|
val resourcePath = buildString {
|
||||||
|
append("/day")
|
||||||
|
append(day.toString().padStart(2, '0'))
|
||||||
|
append(".txt")
|
||||||
|
}
|
||||||
|
val url = javaClass.getResource(resourcePath)
|
||||||
|
return Path.of(url.toURI())
|
||||||
|
}
|
||||||
|
}
|
||||||
14
utils/src/main/resources/logback.xml
Normal file
14
utils/src/main/resources/logback.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<configuration>
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<withJansi>true</withJansi>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%cyan(%d{YYYY-MM-dd HH:mm:ss.SSS}) [%thread] %highlight(%-5level) %green(%logger{36}) - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
<root level="INFO">
|
||||||
|
<appender-ref ref="STDOUT"/>
|
||||||
|
</root>
|
||||||
|
<logger name="io.micronaut" level="INFO"/>
|
||||||
|
<logger name="io.micronaut.context.lifecycle" level="INFO"/>
|
||||||
|
</configuration>
|
||||||
1
utils/src/test/kotlin/Index.kt
Normal file
1
utils/src/test/kotlin/Index.kt
Normal file
@ -0,0 +1 @@
|
|||||||
|
package be.vandewalleh.aoc.utils
|
||||||
80
utils/src/test/kotlin/input/DayTest.kt
Normal file
80
utils/src/test/kotlin/input/DayTest.kt
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package be.vandewalleh.aoc.utils.input
|
||||||
|
|
||||||
|
import com.google.common.jimfs.Jimfs
|
||||||
|
import io.micronaut.context.annotation.Replaces
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlin.io.path.ExperimentalPathApi
|
||||||
|
import kotlin.io.path.writeText
|
||||||
|
|
||||||
|
class DayTest {
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
@Replaces(ResourceLoaderImpl::class)
|
||||||
|
@ExperimentalPathApi
|
||||||
|
class FakeResourceLoader : ResourceLoader {
|
||||||
|
private val inMemoryFs = Jimfs.newFileSystem().apply {
|
||||||
|
getPath("1").writeText("blablabla")
|
||||||
|
getPath("2").writeText("1,+2,3,4,-5")
|
||||||
|
getPath("3")
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
1779
|
||||||
|
1737
|
||||||
|
1537
|
||||||
|
1167
|
||||||
|
1804
|
||||||
|
1873
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
getPath("4")
|
||||||
|
.also { println(it) }
|
||||||
|
.writeText(
|
||||||
|
"""
|
||||||
|
a
|
||||||
|
bb
|
||||||
|
ccc
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun ofDay(day: Int) = inMemoryFs.getPath(day.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Day(1)
|
||||||
|
class TextStringExample(@Text val input: Input<String>)
|
||||||
|
|
||||||
|
@Day(2)
|
||||||
|
class CsvIntArrayExample(@Csv val input: Input<IntArray>)
|
||||||
|
|
||||||
|
@Day(3)
|
||||||
|
class IntLinesExample(@Lines val input: Input<IntArray>)
|
||||||
|
|
||||||
|
@Day(4)
|
||||||
|
class StringLinesExample(@Lines val input: Input<List<String>>)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check @Text String`() {
|
||||||
|
val day = createDay<TextStringExample>()
|
||||||
|
assertThat(day.input.value).isEqualTo("blablabla")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check @Csv IntArray`() {
|
||||||
|
val day = createDay<CsvIntArrayExample>()
|
||||||
|
assertThat(day.input.value).containsExactly(1, +2, 3, 4, -5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check @Lines IntArray`() {
|
||||||
|
val day = createDay<IntLinesExample>()
|
||||||
|
assertThat(day.input.value).containsExactly(1779, 1737, 1537, 1167, 1804, 1873)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `check @Lines strings`() {
|
||||||
|
val day = createDay<StringLinesExample>()
|
||||||
|
assertThat(day.input.value).containsExactly("a", "bb", "ccc")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
4
utils/src/test/resources/junit-platform.properties
Normal file
4
utils/src/test/resources/junit-platform.properties
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
junit.jupiter.testinstance.lifecycle.default=per_class
|
||||||
|
junit.jupiter.execution.parallel.enabled=true
|
||||||
|
junit.jupiter.execution.parallel.mode.default=same_thread
|
||||||
|
junit.jupiter.execution.parallel.mode.classes.default=concurrent
|
||||||
Loading…
x
Reference in New Issue
Block a user