Skip to content

🎁 A library containing common utility classes used in the annual Advent of Code event.

License

Notifications You must be signed in to change notification settings

TomPlum/advent-of-code-libs

Repository files navigation

🎄 Advent of Code Library

GitHub GitHub GitHub

About

A simple Kotlin library for Advent of Code.

After my second year of completing AoC in Kotlin, I extracted common functionality into a library to reduce redundancy. Two packages are published;

advent-of-code-libs

The main utility classes and datastructures to be compiled in the implementation scope.

advent-of-code-test-support

Test utility classes for supporting unit tests and benchmarking for the testImplementation scope.

Implementation Library

Logging

The AdventLogger provides a simple companion-object class that wraps the SLF4J abstraction. The class exposes the usual levels which are as follows

Level Description
Error Designates error events that might still allow the application to continue running.
Warn Designates potentially harmful situations.
Info Designates information messages that highlight the progress of the application at a coarse-grained level.
Debug Designates fine-grained informational events that are most useful to debug the application.
Trace Designates finer-grained informational events than the debug level.

Input De-Serialisation

The InputReader provides an easy way to read the puzzle inputs from the resources directory of the main source set. The reader looks for a file named input.txt in a sub-folder named dayX where X is the day number. The read() function also expects a generic type parameter which changes the type of collection that is returned. The currently supported types are Integer and String.

Math

A common theme in the Advent of Code is maps, which are usually represented on a 2D cartesian grid. Thus, Point2D is a wrapper class for co-ordinates that provides useful translation, transformation and interacting with other points. Likewise, Point3D does a similar thing for 3-Dimensions.

To accompany the Point classes, AdventMap2D and AdventMap3D have been created for Point2D and Point3Drespectively. These classes are essentially cartesian grids backed by a Map of the respective Point class against a MapTile which accepts a generic type. This is usually a Char due to the nature of Advent of Code puzzle inputs.

An example puzzle input of a map that could be read and stored in an AdventMap2D;

..##.......
#...#...#..
.#....#..#.
..#.#...#.#
.#...##..#.
..#.##.....
.#.#.#....#
.#........#
#.##...#...
#...##....#
.#..#...#.#

Solution Running & Benchmarking

A Solution interface is provided so that the solution classes can be passed to the SolutionRunner. The runner executes all the solution implementations and measures their runtimes.

A benchmark is created that produces a report that is appended to the SystemOut. The benchmark utility stores the last run in benchmark.xml in the root of the project directory.

The format and verbosity of the report can be changed via a JVM arg called report. It can have the values verbose or compact. E.g. -Dreport=verbose.

A snippet from a verbose runtime delta report;

- Advent of Code 2020 Solution Report -

[Day 1]
Part 1: 802011
Execution Time: 9ms (+3ms)

Part 2: 248607374
Execution Time: 54ms (+22ms)

[Day 2]
Part 1: 660
Execution Time: 10ms (+4ms)

Part 2: 530
Execution Time: 6ms (+2ms)

...

Average Execution Time: 654ms (+21ms)
Total Execution Time: 16s 365ms (+535ms)

If a previous run cannot be found (I.e. the benchmark.xml is not present), then a delta report cannot be produced. There will be an info log printed, and the report will have the deltas omitted like the following;

Cannot find previous run. Deltas will not be reported.

- Advent of Code 2020 Solution Report -

[Day 1]
Part 1: 802011
Execution Time: 7ms

Part 2: 248607374
Execution Time: 41ms

...

If your terminal supports ANSI escape codes then the deltas will be green or red for increased and decreased runtimes respectively.

Documentation

A complete list of all publicly exposed classes and functions.

Extension Functions

Collections Extension Functions
Function Signature Behaviour
List<Int>.product(): Int
Returns the product of all the integers in the given list.
List<Long>.product(): Long
Returns the product of all the longs in the given list.
IntArray.toDecimal(): Long
Converts the IntArray into its decimal equivalent. Assumes the array contains only 1s and 0s.
          
<S, T> List<S>.cartesianProduct(
  other: List<T>
): List<Pair<S, T>>
          
        
For two sets A and B, the Cartesian product of A and B is denoted by A×B and defined as A×B = { (a,b) | aϵA and bϵB }. Put simply, the Cartesian Product is the multiplication of two sets to form the set of all ordered pairs. Returns the cartesian product of itself and the given set, meaning A and B are this and other.
<T> List<T>.cartesianProductQuadratic(
): List<Pair<T, T>>
        
For two sets A and B, the Cartesian product of A and B is denoted by A×B and defined as A×B = { (a,b) | aϵA and bϵB }. Put simply, the Cartesian Product is the multiplication of two sets to form the set of all ordered pairs. Returns the cartesian product of itself, meaning both A and B are simply this.
<T> List<T>.cartesianProductCubic(
): List<Triple<T, T, T>>
        
For three sets A, B and C, the Cartesian product of A, B and C is denoted by A×B×C and defined as A×B×C = { (p, q, r) | pϵA and qϵB and rϵC }. Put simply, the Cartesian Product is the multiplication of three sets to form the set of all ordered pairs. Returns the cartesian product of itself and the given sets, meaning that A, B & C are all this.
<T> List<T>.cartesianProductCubic(
  second: List<T>, 
  third: List<T>
): List<Triple<T, T, T>>
        
For three sets A, B and C, the Cartesian product of A, B and C is denoted by A×B×C and defined as A×B×C = { (p, q, r) | pϵA and qϵB and rϵC }. Put simply, the Cartesian Product is the multiplication of three sets to form the set of all ordered pairs. Returns the cartesian product of itself and the given sets, meaning both A, B and C are this, second and third respectively.
<T> cartesianProduct(
  vararg sets: List<T>
): List<List<T>>
        
Finds the Cartesian Product of any number of given sets.
<T> Collection<T>.distinctPairs()
returns List<Pair<T, T>>
        
Produces a list of all distinct pairs of elements from the given collection. Pairs are considered distinct irrespective of their order.
<T> Collection<T>.split(
  predicate: (element: T) -> Boolean
): Collection<Collection<T>>
        
Splits a collection based on a given predicate.
          
<L, R> Collection<String>.toVerticalLists(
  parse: (String) -> Pair<L, R>?
): Pair<MutableList<L>, MutableList<R>>
          
        
Parses a collection of Strings (Usually puzzle input lines) vertically to produce two lists.
List<Long>.lcm(): Long
Calculates the lowest common multiple of all the long values of this given list.
Primitive Extension Functions
Function Signature Behaviour
Int.toBinary(bits: Int): IntArray
Converts an Int into its binary equivalent. Pads the number with trailing 0s to reach the number of given bits.
Double.toRadians(): Double
Converts an angle in degrees into radians.
Int.nthBinomialCoefficient(): Int
Calculate the value at the given position in Pascal's triangle. This can be expressed as (n^2 + n) / 2 as a binomial coefficient.
Range Extension Functions
Function Signature Behaviour
IntRange.midpoint(): Int
Finds the midpoint in an IntRange.
Tuple Extension Functions
Function Signature Behaviour
Pair<<Int, Int>.product(): Int
Calculates the product of the integer values of a given Pair.
Pair<Int, Int>.sum(): Int
Calculates the sum of the integer values of a given Pair.
Pair<Long, Long>.sum(): Long
Calculates the sum of the long values of a given Pair.
Triple<Int, Int, Int>.sum(): Int
Calculates the sum of the integer values of a given Triple.
Triple<Int, Int, Int>.product(): Int
Calculates the product of the integer values of a given Triple.

Graphing Algorithms

Function Signature Behaviour
            
<N> dijkstraShortestPath(
    startingPositions: Collection<N>,
    evaluateAdjacency: (
        currentNode: Node<N>
    ) -> Collection<Node<N>>,
    processNode: (
        currentNode: Node<N>, 
        adjacentNode: Node<N>
    ) -> Node<N>,
    terminates: (
        currentNode: Node<N>
    ) -> Boolean
): Int
            
        
Calculates the shortest distance to all nodes in a weighted-graph from the given starting Positions and terminates based on the given predicate.

Test Support Library

VisualVM Support

Running unit tests with JUnit via VisualVM can be difficult due to the time taken to launch the VisualVM process, attach to the running Java thread, and configure sampling/profiling. There doesn't seem to be a particularly elegant process for this other than telling the thread to wait.

One problem I ran into (and seemingly many others online too) was that JUnit5 bootstrapped and ran my test so quickly that it finished before VisualVM even had a chance to boot-up. A common solution I'd seen was to simply add a Thread.sleep(x) line at the start of the test method. Although this is the solution I technically went with, I abstracted it into a @WaitForVisualVM annotation and created a custom implementation of the Jupiter APIs BeforeTestExecutionCallback interface called SupportsVisualVM.kt which can be added to a test-suite class using the @ExtendWith annotation.

This kept things inline with the 'enterprise-style' aspect of my codebase as it did the following;

  • Wrapped the dubious Thread.sleep() call with a self-explanatory annotation (and is also documented).
  • Removed noise from the test method and ensured that it always runs before test-execution.
  • Allows developers to easily disable all waiting for tests in a suite by simply removing the support extension.
  • Makes it easier to refactor in the future the as implementation specifics are encapsulated in the annotation.

Release Instructions

  • Make your code changes in the master branch (or feature branch)
  • Update the release version in the build.gradle
  • Rebase off of the release branch to ensure a fast-forward merge
  • Merge to release branch once ready
  • Create a new release on GitHub, set the new version tag and draft the release
  • This will trigger GitHub actions release pipeline, wait for run to complete

To-Do List

  • Investigate and fix big integer printing, seems to be truncating
  • A test support class for testing solutions. Accepts a solution and expected answers for p1, p2
  • Added graph/node objets for graphing algos like Djikstra
  • Added a findTile() function to the AdventMaps so you can pass a predicate to find or null
  • Automatic scanning for Solution classes for the SolutionRunner
  • AdventMap3D xMin() returns only where z=0? Can this be removed?
  • AdventMap ordMin() methods use minByOrNull instead of minBy
  • Move Formulae (lcm, gcd) from aoc-2019 to math package

About

🎁 A library containing common utility classes used in the annual Advent of Code event.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages