Skip to content

Commit

Permalink
Extract common code to Challenge class
Browse files Browse the repository at this point in the history
  • Loading branch information
FWDekker committed Dec 16, 2024
1 parent 945db57 commit 7967701
Show file tree
Hide file tree
Showing 20 changed files with 224 additions and 144 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ My solutions to the [Advent of Code](https://adventofcode.com/), years 2023 and
Also includes my solutions for [Project Euler](https://projecteuler.net/).
* [The source code is here.](https://github.com/FWDekker/project-euler) (In a private repo.)

## Git submodules
## Git submodules (aka: How to clone)
Advent of Code does not allow sharing puzzle inputs, and Project Euler does not allow sharing solutions.
Therefore, these are stored in separate, private repositories.
These repositories are included as [git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules).
Expand All @@ -34,7 +34,7 @@ If you have made changes in a submodule rooted at `dir/`, you have to `cd dir` b
After doing so, the root repository will be outdated, since it still points to a specific commit, not to the head of your specific branch.
You will have to update the submodule reference manually using a plain `git add dir` from within the root repository.

## Gradle sub-projects
## Gradle sub-projects (aka: How to run)
To allow Advent of Code and Project Euler to be built separately, but still use a common codebase, the repository has been structured using [Gradle subprojects](https://docs.gradle.org/current/userguide/intro_multi_project_builds.html).
This repository has the following subprojects:
* [`buildSrc`](https://github.com/FWDekker/aoc/tree/main/buildSrc): Common build logic for all subprojects.
Expand All @@ -59,4 +59,6 @@ gradlew :aoc:test
gradlew :aoc:test -Pkotest.tags="Foo"
# Run all tests for AoC 2024
gradlew test --tests "com.fwdekker.aoc.y2024.*"
# Run all tests for AoC 2024 (not fully implemented yet)
gradlew :aoc:test -Pkotest.tags="2024"
```
3 changes: 2 additions & 1 deletion aoc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ plugins {

dependencies {
implementation(project(":std"))

implementation("org.jgrapht:jgrapht-core:1.5.2")

testImplementation(project(":std", "test"))
}

application {
Expand Down
56 changes: 17 additions & 39 deletions aoc/src/main/kotlin/com/fwdekker/aoc/Day.kt
Original file line number Diff line number Diff line change
@@ -1,56 +1,34 @@
package com.fwdekker.aoc

import com.fwdekker.std.grid.Cardinal
import com.fwdekker.std.grid.Direction
import com.fwdekker.std.grid.Ordinal
import kotlin.time.TimeSource
import kotlin.time.measureTimedValue
import com.fwdekker.std.Challenge


/**
* Convenience class for invoking the code for any particular day and part with a given resource.
*
* @param year the year of advent of code this day belongs to
* @param day the day of advent of code this object corresponds to
* @param sample the sample number to solve for, or `null` to use the full problem
*/
abstract class Day {
private val time = TimeSource.Monotonic
private val mark = TimeSource.Monotonic.markNow()


/**
* Runs and prints both parts and the time it took to execute them.
*/
fun run() {
println("Instantiation: Complete. (${(time.markNow() - mark).inWholeMilliseconds} ms)")
measureTimedValue { part1() }
.also { println("Part one: ${it.value} (in ${it.duration.inWholeMilliseconds} ms)") }
measureTimedValue { part2() }
.also { println("Part two: ${it.value} (in ${it.duration.inWholeMilliseconds} ms)") }
}
abstract class Day(val year: Int? = null, val day: Int? = null, val sample: Int? = null) : Challenge(2) {
override val resource: String get() = resource(year!!, day!!, sample)


override fun runPart(number: Int): Any =
when (number) {
1 -> part1()
2 -> part2()
else -> error("Invalid part number $number.")
}

/**
* Returns the output for the first part of this day.
*/
abstract fun part1(): Any

/**
* Returns the output for the second part of this day.
*/
abstract fun part2(): Any


/**
* Holds constants.
*/
companion object {
@Suppress("unused")
private val ignoreMe = Triple(Cardinal, Ordinal, Direction) // TODO: Remove workaround for KT-59723


/**
* Shorthand for returning the resource for the given [year] and [day].
*
* Setting [sample] to `null` returns the final difficult input, whereas all other values return easier sample
* inputs.
*/
@Deprecated("Use non-static zero-parameter call instead.", replaceWith = ReplaceWith("resource()"))
// TODO: Once no classes use this method anymore, make class fields `year` and `day` non-nullable
fun resource(year: Int, day: Int, sample: Int? = null) =
"/y$year/Day${day}"
.let { if (sample != null) "${it}Sample${sample}" else it }
Expand Down
8 changes: 4 additions & 4 deletions aoc/src/main/kotlin/com/fwdekker/aoc/y2023/Day24.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.fwdekker.aoc.Day
import com.fwdekker.std.collections.asTriple
import com.fwdekker.std.linesNotBlank
import com.fwdekker.std.maths.cartesian
import com.fwdekker.std.read
import com.fwdekker.std.toLongs
import java.math.BigDecimal
import java.math.MathContext
Expand All @@ -16,12 +15,13 @@ private typealias LMatrix = List<LVec>
private typealias BDMatrix = List<List<BigDecimal>>


// See https://adventofcode.com/2023/day/24
class Day24(
resource: String = resource(2023, 24),
sample: Int? = null,
private val coordinateRange: LongRange = 200000000000000L..400000000000000L,
) : Day() {
) : Day(year = 2023, day = 24, sample = sample) {
private val stones =
read(resource).linesNotBlank()
input.linesNotBlank()
.map { line -> line.split(Regex("\\s+@\\s+")).map { coords -> coords.toLongs(Regex(",\\s+")) } }


Expand Down
10 changes: 5 additions & 5 deletions aoc/src/main/kotlin/com/fwdekker/aoc/y2024/Day11.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import com.fwdekker.aoc.Day
import com.fwdekker.std.collections.fold
import com.fwdekker.std.collections.map
import com.fwdekker.std.foldSelf
import com.fwdekker.std.read
import com.fwdekker.std.sections
import com.fwdekker.std.splitAtIndex
import com.fwdekker.std.toLongs


class Day11(resource: String = resource(2024, 11)) : Day() {
private val input = read(resource).sections()
private val blinks = input[0][0].toInt()
private val stones = input[1][0].toLongs(' ')
// See https://adventofcode.com/2024/day/11
class Day11(sample: Int? = null) : Day(year = 2024, day = 11, sample = sample) {
private val sections = input.sections()
private val blinks = sections[0][0].toInt()
private val stones = sections[1][0].toLongs(' ')


override fun part1(): Long = blink(blinks, stones).values.sum()
Expand Down
12 changes: 6 additions & 6 deletions aoc/src/main/kotlin/com/fwdekker/aoc/y2024/Day14.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import com.fwdekker.std.linesNotBlank
import com.fwdekker.std.maths.plus
import com.fwdekker.std.maths.product
import com.fwdekker.std.maths.wrapMod
import com.fwdekker.std.read
import com.fwdekker.std.runningFoldSelf
import kotlin.math.pow


// See https://adventofcode.com/2024/day/14
class Day14(resource: String = resource(2024, 14)) : Day() {
private val width = 101
private val height = 103

private val robots = read(resource).linesNotBlank().allInts().map { Pair(it[0], it[1]) to Pair(it[2], it[3]) }
class Day14(
sample: Int? = null,
private val width: Int = 101,
private val height: Int = 103,
) : Day(year = 2024, day = 14, sample = sample) {
private val robots = input.linesNotBlank().allInts().map { Pair(it[0], it[1]) to Pair(it[2], it[3]) }


override fun part1(): Long {
Expand Down
9 changes: 4 additions & 5 deletions aoc/src/main/kotlin/com/fwdekker/aoc/y2024/Day15.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ import com.fwdekker.std.grid.row
import com.fwdekker.std.grid.set
import com.fwdekker.std.grid.west
import com.fwdekker.std.maths.Graph
import com.fwdekker.std.read
import com.fwdekker.std.sections


// See https://adventofcode.com/2024/day/15
class Day15(resource: String = resource(2024, 15)) : Day() {
private val input = read(resource).sections()
private val chart = input[0].map { it.toList() }
private val moves = input[1].joinToString("")
class Day15(sample: Int? = null) : Day(year = 2024, day = 15, sample = sample) {
private val sections = input.sections()
private val chart = sections[0].map { it.toList() }
private val moves = sections[1].joinToString("")


override fun part1(): Long = chart.map { it.toMutableList() }.releaseFish()
Expand Down
5 changes: 2 additions & 3 deletions aoc/src/main/kotlin/com/fwdekker/aoc/y2024/Day16.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import com.fwdekker.std.grid.coordsOf
import com.fwdekker.std.grid.get
import com.fwdekker.std.grid.toChart
import com.fwdekker.std.maths.Graph
import com.fwdekker.std.read


// See https://adventofcode.com/2024/day/16
class Day16(resource: String = resource(2024, 16)) : Day() {
private val chart = read(resource).toChart()
class Day16(sample: Int? = null) : Day(year = 2024, day = 16, sample = sample) {
private val chart = input.toChart()
private val start = Heading(chart.coordsOf('S'), East)
private val ends = chart.coordsOf('E').let { end -> Cardinal.entries.map { Heading(end, it) } }

Expand Down
5 changes: 2 additions & 3 deletions aoc/src/main/kotlin/com/fwdekker/aoc/y2024/Day999.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.fwdekker.aoc.y2024

import com.fwdekker.aoc.Day
import com.fwdekker.std.read


// See https://adventofcode.com/2024/day/999
class Day999(resource: String = resource(2024, 999)) : Day() {
private val input = read(resource)
class Day999(sample: Int? = null) : Day(year = 2024, day = 999, sample = sample) {
private val string = input


override fun part1() = 0
Expand Down
1 change: 1 addition & 0 deletions aoc/src/test/kotlin/com/fwdekker/aoc/DayTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.kotest.matchers.shouldBe
* @param tests tests to run, expressed as an input file, a reference to the part to run, and the expected output
* @constructor
*/
@Deprecated("Use 'ChallengeTest' instead.", replaceWith = ReplaceWith("ChallengeTest"))
abstract class DayTest(day: (String) -> Day, tests: Collection<Triple<String, (Day) -> Any, Any>>) : FunSpec({
withData(
nameFn = { (file, part, expected) -> "($file, ${if (part == Day::part1) "part 1" else "part 2"}) = $expected" },
Expand Down
34 changes: 13 additions & 21 deletions aoc/src/test/kotlin/com/fwdekker/aoc/y2023/Day24Test.kt
Original file line number Diff line number Diff line change
@@ -1,28 +1,20 @@
package com.fwdekker.aoc.y2023

import com.fwdekker.aoc.Day
import com.fwdekker.aoc.Day.Companion.resource
import com.fwdekker.aoc.DayTest
import com.fwdekker.std.ChallengeTest
import com.fwdekker.std.case
import io.kotest.core.annotation.Tags


/**
* Tests for samples of [Day24].
*/
object Day24SamplesTest : DayTest(
{ resource -> Day24(resource, 7L..27L) },
@Tags("2023")
object Day24Test : ChallengeTest(
{ sample ->
if (sample == 1) Day24(sample, 7L..27L)
else Day24(sample)
},
listOf(
Triple(resource(2023, 24, sample = 1), Day::part1, 2L),
Triple(resource(2023, 24, sample = 1), Day::part2, 47L),
)
)

/**
* Tests for [Day24].
*/
object Day24Test : DayTest(
::Day24,
listOf(
Triple(resource(2023, 24), Day::part1, 16727L),
Triple(resource(2023, 24), Day::part2, 606772018765659L),
case(part = 1, sample = 1) to 2L,
case(part = 2, sample = 1) to 47L,
case(part = 1) to 16727L,
case(part = 2) to 606772018765659L,
)
)
19 changes: 9 additions & 10 deletions aoc/src/test/kotlin/com/fwdekker/aoc/y2024/Day11Test.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.fwdekker.aoc.y2024

import com.fwdekker.aoc.Day
import com.fwdekker.aoc.DayTest
import com.fwdekker.std.ChallengeTest
import com.fwdekker.std.case
import io.kotest.core.annotation.Tags


/**
* Tests for [Day11].
*/
object Day11Test : DayTest(
@Tags("2024")
object Day11Test : ChallengeTest(
::Day11,
listOf(
Triple(Day.resource(2024, 11, sample = 1), Day::part1, 7L),
Triple(Day.resource(2024, 11, sample = 2), Day::part1, 55312L),
Triple(Day.resource(2024, 11), Day::part1, 216996L),
Triple(Day.resource(2024, 11), Day::part2, 257335372288947L),
case(part = 1, sample = 1) to 7L,
case(part = 1, sample = 2) to 55312L,
case(part = 1) to 216996L,
case(part = 2) to 257335372288947L,
)
)
23 changes: 12 additions & 11 deletions aoc/src/test/kotlin/com/fwdekker/aoc/y2024/Day14Test.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
package com.fwdekker.aoc.y2024

import com.fwdekker.aoc.Day
import com.fwdekker.aoc.DayTest
import io.kotest.core.annotation.Ignored
import java.math.BigInteger
import com.fwdekker.std.ChallengeTest
import com.fwdekker.std.case
import io.kotest.core.annotation.Tags


/**
* Tests for [Day14].
*/
object Day14Test : DayTest(
::Day14,
@Tags("2024")
object Day14Test : ChallengeTest(
{ sample ->
if (sample == 1) Day14(sample, width = 11, height = 7)
else Day14(sample)
},
listOf(
Triple(Day.resource(2024, 14), Day::part1, 217328832),
Triple(Day.resource(2024, 14), Day::part2, 7412),
case(part = 1, sample = 1) to 12,
case(part = 1) to 217328832,
case(part = 2) to 7412,
)
)
21 changes: 10 additions & 11 deletions aoc/src/test/kotlin/com/fwdekker/aoc/y2024/Day15Test.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package com.fwdekker.aoc.y2024

import com.fwdekker.aoc.Day
import com.fwdekker.aoc.DayTest
import com.fwdekker.std.ChallengeTest
import com.fwdekker.std.case
import io.kotest.core.annotation.Tags


/**
* Tests for [Day15].
*/
object Day15Test : DayTest(
@Tags("2024")
object Day15Test : ChallengeTest(
::Day15,
listOf(
Triple(Day.resource(2024, 15, sample = 1), Day::part1, 2028L),
Triple(Day.resource(2024, 15, sample = 2), Day::part1, 10092L),
Triple(Day.resource(2024, 15, sample = 2), Day::part2, 9021L),
Triple(Day.resource(2024, 15), Day::part1, 1471826L),
Triple(Day.resource(2024, 15), Day::part2, 1457703L),
case(part = 1, sample = 1) to 2028L,
case(part = 1, sample = 2) to 10092L,
case(part = 2, sample = 2) to 9021L,
case(part = 1) to 1471826L,
case(part = 2) to 1457703L,
)
)
Loading

0 comments on commit 7967701

Please sign in to comment.