Skip to content

Commit

Permalink
Automation
Browse files Browse the repository at this point in the history
Added puzzle download and submission automation. New structure for puzzle classes and more
  • Loading branch information
hibob224 committed Dec 27, 2024
1 parent 46a9d33 commit 28e40cc
Show file tree
Hide file tree
Showing 16 changed files with 212 additions and 43 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
input.txt filter=git-crypt diff=git-crypt
correctedinput.txt filter=git-crypt diff=git-crypt
sample.txt filter=git-crypt diff=git-crypt
d*.txt filter=git-crypt diff=git-crypt
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ build/
.idea/
.gradle/
out.txt
.DS_Store
.DS_Store
.env
submission.cache
17 changes: 6 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '2.0.21'
id 'org.jetbrains.kotlin.plugin.serialization' version '2.0.21'
id 'de.undercouch.download' version '5.6.0'
}

group 'com.galajeu'
Expand All @@ -16,6 +15,12 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib"
implementation 'org.junit.jupiter:junit-jupiter:5.11.3'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0"
def ktor = "3.0.2"
implementation "io.ktor:ktor-client-core:$ktor"
implementation "io.ktor:ktor-client-okhttp:$ktor"
implementation "ch.qos.logback:logback-classic:1.5.15"
implementation "io.github.cdimascio:dotenv-kotlin:6.5.0"
testImplementation "io.kotest:kotest-assertions-core:6.0.0.M1"
}

test {
Expand All @@ -36,13 +41,3 @@ compileTestKotlin {
jvmTarget = "11"
}
}

tasks.register('fetchInput', Download) {
Calendar now = Calendar.getInstance()
String year = project.getProperties().getOrDefault("year", now.get(Calendar.YEAR))
String day = project.getProperties().getOrDefault("day", now.get(Calendar.DAY_OF_MONTH))
String paddedDay = day.padLeft(2, '0')
src "https://adventofcode.com/$year/day/$day/input"
dest new File(rootDir, "src/main/kotlin/y$year/day$paddedDay/input.txt")
header("Cookie", "session=$aocSession")
}
1 change: 1 addition & 0 deletions input/examples/y2015/d12.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"d":"red","e":[1,2,3,4],"f":5}
1 change: 1 addition & 0 deletions input/y2015/d10.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1113122113
1 change: 1 addition & 0 deletions input/y2015/d12.txt

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions src/main/kotlin/networking/Client.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package networking

import io.github.cdimascio.dotenv.Dotenv
import io.ktor.client.*
import io.ktor.client.engine.okhttp.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*

val client = HttpClient(OkHttp) {
followRedirects = false
install(UserAgent) {
agent = "https://github.com/hibob224/aoc/ by contact@galajeu.com"
}
defaultRequest {
cookie("session", Dotenv.load().get("aocSession"))
}
}
17 changes: 0 additions & 17 deletions src/main/kotlin/template/DayXX.kt

This file was deleted.

27 changes: 27 additions & 0 deletions src/main/kotlin/template/InputProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package template

import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.runBlocking
import networking.client
import java.io.File

object InputProvider {

fun provideInput(year: Int, day: Int, example: Boolean): String = if (example) {
File("input/examples/y$year/d$day.txt")
} else {
File("input/y$year/d$day.txt")
.also {
if (!it.exists()) downloadInput(year, day, it)
}
}.readText()

private fun downloadInput(year: Int, day: Int, out: File) = runBlocking {
println("Downloading input: Y${year}D$day")
val response = client.get("https://adventofcode.com/$year/day/$day/input")
require(response.status.isSuccess())
out.writeBytes(response.bodyAsBytes())
}
}
16 changes: 16 additions & 0 deletions src/main/kotlin/template/Puzzle.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package template

abstract class Puzzle<P1, P2>(
val year: Int,
val day: Int,
val example: Boolean = false,
) {

val rawInput = InputProvider.provideInput(year, day, example)

abstract val input: Any

abstract fun solvePartOne(): P1

abstract fun solvePartTwo(): P2
}
65 changes: 65 additions & 0 deletions src/main/kotlin/template/Solver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package template

import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.runBlocking
import networking.client
import utils.print
import java.io.File
import kotlin.time.measureTimedValue

fun solve(puzzleProvider: () -> Puzzle<*, *>) {
val puzzle = puzzleProvider()
val (p1, p1d) = measureTimedValue { puzzle.solvePartOne() }
val (p2, p2d) = measureTimedValue { puzzle.solvePartTwo() }
println("Solving Y${puzzle.year}-D${puzzle.day}")
println("Part One: ${p1!!.bold()} - ${p1d.inWholeMilliseconds} ms")
submitAnswer(p1.toString(), puzzle.year, puzzle.day, 1, puzzle.example)
println("Part Two: ${p2!!.bold()} - ${p2d.inWholeMilliseconds} ms")
submitAnswer(p2.toString(), puzzle.year, puzzle.day, 2, puzzle.example)
}

private fun submitAnswer(answer: String, year: Int, day: Int, part: Int, example: Boolean) {
if (isSolved(year, day, part) || example) return
print("Submit answer? (Yes/No/Ignore): ")
val inp = readln().lowercase()
when (inp) {
"y" -> submitAnswer(answer, year, day, part)
"i" -> markSubmitted(year, day, part)
}
}

private fun submitAnswer(answer: String, year: Int, day: Int, part: Int) = runBlocking {
val response = client.post("https://adventofcode.com/$year/day/$day/answer") {
setBody(parameters {
append("level", part.toString())
append("answer", answer)
}.formUrlEncode())
contentType(ContentType.Application.FormUrlEncoded)
}

val result = """article>(.*)</article""".toRegex()
.find(response.bodyAsText())!!
.groupValues[1]
.replace("""<a href.*?</a>""".toRegex(), "")
.replace("<p>", "")
.replace("</p>", "")
.print()

if ("That's the right answer!" in result) {
markSubmitted(year, day, part)
}
}

private fun isSolved(year: Int, day: Int, part: Int): Boolean {
val key = "${year}.${day}.${part}"
val submission = File("submission.cache").readLines()
return key in submission
}

private fun markSubmitted(year: Int, day: Int, part: Int) {
File("submission.cache").appendText("${year}.${day}.$part\n")
}

private fun Any.bold(): String = "\u001B[33;1m$this\u001B[0m"
2 changes: 1 addition & 1 deletion src/main/kotlin/utils/Extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ fun findSubsequenceIndex(mainList: List<Long>, subList: List<Long>): Int {
return -1
}

fun Any?.print() = also(::println)
fun <T> T.print() = also(::println)

fun <T> List<T>.toPair(): Pair<T, T> {
check(size == 2)
Expand Down
37 changes: 37 additions & 0 deletions src/main/kotlin/y2015/Day12.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package y2015

import kotlinx.serialization.json.*
import template.Puzzle
import template.solve
import utils.orZero

fun main() = solve { Day12() }

class Day12 : Puzzle<Int, Int>(year = 2015, day = 12, example = false) {

override val input = rawInput
private val numberRegex = """-?\d+""".toRegex()

override fun solvePartOne(): Int {
return numberRegex
.findAll(input)
.map { it.value.toInt() }
.sum()
}

override fun solvePartTwo(): Int {
return Json.parseToJsonElement(input).sum()
}

private fun JsonElement.sum(): Int {
return when (this) {
is JsonObject -> if (values.any { (it as? JsonPrimitive)?.content == "red" }) {
0
} else {
values.sumOf { it.sum() }
}
is JsonArray -> sumOf { it.sum() }
is JsonPrimitive -> intOrNull.orZero()
}
}
}
21 changes: 8 additions & 13 deletions src/main/kotlin/y2015/day10/Day10.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
package y2015.day10

fun main() {
val day = Day10()
println("Part one: " + day.solvePartOne())
println("Part two: " + day.solvePartTwo())
}
import template.Puzzle
import template.solve

fun main() = solve { Day10() }

class Day10 {
class Day10 : Puzzle<Int, Int>(2015, 10) {

private val regex = """(\d)\1*""".toRegex()
private val input = "1113122113"
override val input = rawInput

fun solvePartOne(): Int {
return generateSequence(input, ::lookAndSee).elementAt(40).length
}
override fun solvePartOne(): Int = generateSequence(input, ::lookAndSee).elementAt(40).length

fun solvePartTwo(): Int {
return generateSequence(input, ::lookAndSee).elementAt(50).length
}
override fun solvePartTwo(): Int = generateSequence(input, ::lookAndSee).elementAt(50).length

private fun lookAndSee(inp: String): String {
val matches = regex.findAll(inp)
Expand Down
13 changes: 13 additions & 0 deletions src/test/kotlin/PuzzleTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import io.kotest.matchers.shouldBe
import template.Puzzle

abstract class PuzzleTest<P1, P2> {

abstract val puzzle: () -> Puzzle<P1, P2>

fun runTest(p1: P1, p2: P2) {
val p = puzzle()
p.solvePartOne() shouldBe p1
p.solvePartTwo() shouldBe p2
}
}
15 changes: 15 additions & 0 deletions src/test/kotlin/y2015/Day12Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package y2015

import PuzzleTest
import org.junit.jupiter.api.Test
import template.Puzzle

class Day12Test : PuzzleTest<Int, Int>() {

override val puzzle: () -> Puzzle<Int, Int> = { Day12() }

@Test
fun `test y2015d12`() {
runTest(111754, 65402)
}
}

0 comments on commit 28e40cc

Please sign in to comment.