Add base structure and library stuff from last year

This commit is contained in:
Alexander Gehrke 2021-12-02 23:34:40 +01:00
commit 9017c4379c
35 changed files with 452 additions and 0 deletions

6
.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.bloop
.bsp
target/
project/project
project/bloop.sbt
project/metals.sbt

20
build.sbt Normal file
View file

@ -0,0 +1,20 @@
lazy val root = project
.in(file("."))
.settings(
name := "aoc2021",
version := "0.1.0",
scalaVersion := "3.1.0",
scalacOptions ++= Seq(
"-Yexplicit-nulls",
"-language:strict-equality",
"-deprecation",
),
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.6.1",
"org.typelevel" %% "cats-effect" % "3.1.1",
"org.typelevel" %% "kittens" % "3.0.0-M1",
)
)
Runtime / unmanagedSources += baseDirectory.value / "input"

1
project/build.properties Normal file
View file

@ -0,0 +1 @@
sbt.version=1.5.3

2
project/plugins.sbt Normal file
View file

@ -0,0 +1,2 @@
//addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.29")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3")

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day1(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day10(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day11(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day12(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day13(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day14(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day15(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day16(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day17(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day18(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day19(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day2(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day20(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day21(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day22(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day23(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day24(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day25(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day3(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day4(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day5(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day6(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day7(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day8(using InputSource): Unit = ???

View file

@ -0,0 +1,6 @@
package de.qwertyuiop.aoc.`2021`
import de.qwertyuiop.aoc.lib.*
import cats.*, cats.implicits.given
def day9(using InputSource): Unit = ???

View file

@ -0,0 +1,37 @@
package de.qwertyuiop.aoc
import de.qwertyuiop.aoc.lib._
import de.qwertyuiop.aoc.`2021`._
@main def runDay(inputDir: String, day: Int, sample: Int*): Unit =
given InputSource = inputSource(inputDir, day, sample.headOption)
day match {
case 1 => day1
case 2 => day2
case 3 => day3
case 4 => day4
case 5 => day5
case 6 => day6
case 7 => day7
case 8 => day8
case 9 => day9
case 10 => day10
case 11 => day11
case 12 => day12
case 13 => day13
case 14 => day14
case 15 => day15
case 16 => day16
case 17 => day17
case 18 => day18
case 19 => day19
case 20 => day20
case 21 => day21
case 22 => day22
case 23 => day23
case 24 => day24
case 25 => day25
case _ => println("No such day implemented")
}
def inputSource(inputDir:String, day: Int, sample: Option[Int]) =
sample.map(InputSource.SampleLocation(inputDir, day, _))
.getOrElse(InputSource.Location(inputDir, day))

View file

@ -0,0 +1,28 @@
package de.qwertyuiop.aoc.lib
import scala.annotation.targetName
/* Wrap a vector to create a limited size ring buffer */
case class RingBuffer[A](underlying: Vector[A], maxSize: Int):
/* pass through any methods to the underlying vector, except appending and
* prepending single elements */
export underlying.{ :+ => _, appended => _, +: => _, prepended => _, _ }
inline def full: Boolean = underlying.size == maxSize
@targetName("appended") def :+[B >: A](b: B): RingBuffer[B] =
val appendedVec = (if full then underlying.tail else underlying) :+ b
new RingBuffer(appendedVec, maxSize)
@targetName("prepended") def +:[B >: A](b: B): RingBuffer[B] =
val prependedVec = b +: (if full then underlying.init else underlying)
new RingBuffer(prependedVec, maxSize)
object RingBuffer:
def apply[A](underlying: Vector[A], maxSize: Int): RingBuffer[A] =
new RingBuffer(
if underlying.size > maxSize then underlying.view.slice(0, maxSize).toVector
else underlying,
maxSize
)

View file

@ -0,0 +1,23 @@
package de.qwertyuiop.aoc.lib
/* for splitting input with separator lines */
extension [A](input: List[A])(using CanEqual[A,A])
def split(separator: A, keepSeparator: Boolean = false): LazyList[List[A]] =
input.span(_ != separator) match
case (Nil, Nil) => LazyList()
case (h, Nil) => LazyList(h)
case (h, tail @ (_ :: t)) =>
h #:: (if keepSeparator then tail else t).split(separator)
/* Using -Yexplicit-nulls isn't really ready for use with the java standard
* library. e.g. String doesn't have `@NotNull` annotations for its methods
*/
extension (s: String)
def splitOnce(regex: String): Option[(String, String)] =
s.split(regex, 2) match
case Array(a, b) => Some((a.nn, b.nn))
case _ => None
extension [K,V,W](map: Map[K,V])
def mapValuesS(f: V => W): Map[K, W] = map.view.mapValues(f).toMap

View file

@ -0,0 +1,31 @@
package de.qwertyuiop.aoc.lib
import scala.io.Source
enum InputSource:
case Location(inputDir: String, day: Int)
case Fixed(lines: List[String])
case SampleLocation(inputDir: String, day: Int, sample: Int)
def inputLines(using src: InputSource): Iterator[String] =
src match
case InputSource.Location(inputDir, day) => scala.io.Source.fromFile(s"${inputDir}/day${day}.txt").getLines
case InputSource.Fixed(input) => input.iterator
case InputSource.SampleLocation(inputDir, day, sample) => scala.io.Source.fromFile(s"${inputDir}/day${day}-sample${sample}.txt").getLines
def simpleInput(using l: InputSource): String = inputLines.mkString
def input[A](format: String => A = identity)(using l: InputSource): List[A] =
inputF(format)(List)
def inputF[A, C[_]](format: String => A = identity)(coll: collection.Factory[A, C[A]])(using l: InputSource): C[A] =
inputLines.map(format).to(coll)
def flatInput[A](format: String => List[A] = List.apply)(using l: InputSource): List[A] =
flatInputF(format)(List)
def flatInputF[A, C[_]](format: String => IterableOnce[A] = identity)(coll: collection.Factory[A, C[A]])(using l: InputSource): C[A] =
inputLines.flatMap(format).to(coll)
def boolChar(trueChar: Char): String => Vector[Boolean] = _.map(_ == trueChar).toVector

View file

@ -0,0 +1,9 @@
package de.qwertyuiop.aoc.lib
import scala.collection.mutable
def memoize[I, O](f: I => O): I => O = new mutable.HashMap[I, O]() {self =>
override def apply(key: I) = self.synchronized(getOrElseUpdate(key, f(key)))
}
def repeat[T](n: Int)(f: T => T): T => T = Function.chain(Seq.fill(n)(f))

View file

@ -0,0 +1,145 @@
package de.qwertyuiop.aoc.lib
import cats.*, cats.implicits.given
import cats.derived.semiauto
object Vectors:
import scala.Numeric.Implicits.given
import scala.compiletime.ops.int.*
import Directions.*
opaque type Vec2D[T] = (T, T)
opaque type Vec3D[T] = (T, T, T)
opaque type Vec4D[T] = (T, T, T, T)
def Vec2D[T](x: T, y: T): Vec2D[T] = (x,y)
def Vec3D[T](x: T, y: T, z: T): Vec3D[T] = (x,y,z)
def Vec4D[T](x: T, y: T, z: T, w: T): Vec4D[T] = (x,y,z,w)
trait Vec[T]:
extension (v: T)
def +(w: T): T
def neighbours: List[T]
given [T : Show]: Show[Vec2D[T]] with
def show(v: Vec2D[T]): String = show"Vec2D(${v._1}, ${v._2})"
given [T : Show]: Show[Vec3D[T]] with
def show(v: Vec3D[T]): String = show"Vec3D(${v._1}, ${v._2}, ${v._3})"
given [T : Show]: Show[Vec4D[T]] with
def show(v: Vec4D[T]): String = show"Vec4D(${v._1}, ${v._2}, ${v._3}, ${v._4})"
given [T: Numeric]: Vec[Vec2D[T]] with
extension (v: Vec2D[T])
def +(w: Vec2D[T]): Vec2D[T] = (v._1 + w._1, v._2 + w._2)
def -(w: Vec2D[T]): Vec2D[T] = (v._1 - w._1, v._2 - w._2)
def *(x: T): Vec2D[T] = (v._1 * x, v._2 * x)
def x: T = v._1
def y: T = v._2
def rot(deg: Dir): Vec2D[T] = deg match
case North => (-v._2, v._1)
case West => (-v._1, -v._2)
case South => (v._2, -v._1)
case East => v
def left(deg: Int): Vec2D[T] = repeat[Vec2D[T]](deg / 90)(_.rotLeft)(v)
def right(deg: Int): Vec2D[T] = repeat[Vec2D[T]](deg / 90)(_.rotRight)(v)
def rotLeft: Vec2D[T] = (-v._2, v._1)
def rotRight: Vec2D[T] = (v._2, -v._1)
def move(dir: Dir, dist: T): Vec2D[T] = dir match
case North => (v._1, v._2 + dist)
case South => (v._1, v._2 - dist)
case East => (v._1 + dist, v._2)
case West => (v._1 - dist, v._2)
def manhattan: T = v._1.abs + v._2.abs
def neighbours: List[Vec2D[T]] =
neighbourCoords(2).map(n => v + (n(0), n(1)))
given [T: Monoid]: Monoid[Vec2D[T]] = semiauto.monoid
given [T: Monoid]: Monoid[Vec3D[T]] = semiauto.monoid
given [T: Monoid]: Monoid[Vec4D[T]] = semiauto.monoid
given [T: Numeric]: Vec[Vec3D[T]] with
extension (v: Vec3D[T])
def +(w: Vec3D[T]): Vec3D[T] = (v._1 + w._1, v._2 + w._2, v._3 + w._3)
def neighbours: List[Vec3D[T]] =
neighbourCoords(3).map(n => v + (n(0), n(1), n(2)))
def x: T = v._1
def y: T = v._2
def z: T = v._3
given [T: Numeric]: Vec[Vec4D[T]] with
extension (v: Vec4D[T])
def +(u: Vec4D[T]): Vec4D[T] = (v._1 + u._1, v._2 + u._2, v._3 + u._3, v._4 + u._4)
def neighbours: List[Vec4D[T]] =
neighbourCoords(4).map(n => v + (n(0), n(1), n(2), n(3)))
def x: T = v._1
def y: T = v._2
def z: T = v._3
def w: T = v._4
/* compute these only once per type and dimension*/
import scala.collection.mutable
private var _neighbourCache = mutable.Map[(Numeric[_], Int), List[List[_]]]()
def neighbourCoords[T](dim: Int)(using n: Numeric[T]): List[List[T]] =
_neighbourCache.get((n, dim)) match
case None =>
val self = List.fill(dim)(n.zero)
val neighs = List.fill(dim)(List(-n.one, n.zero, n.one)).sequence[List, T]
.filter(_ != self)
_neighbourCache.put((n, dim), neighs)
neighs
case Some(neighs) => neighs.asInstanceOf[List[List[T]]]
object Directions:
opaque type Dir = Int
val East: Dir = 0
val North: Dir = 90
val West: Dir = 180
val South: Dir = 270
extension (c: Char)
def cardinal: Dir = c match
case 'E' => East
case 'N' => North
case 'W' => West
case 'S' => South
def Dir(deg: Int): Dir = (deg % 360 + 360) % 360
private val rotationOrder = Vector(North, West, South, East, North, West)
extension (dir: Dir)
def +(deg: Int|Dir): Dir = (dir + deg) % 360
def -(deg: Int|Dir): Dir = ((dir - deg) % 360 + 360) % 360
def unary_- : Dir = 360 - dir
def str: String = dir match
case East => "East"
case North => "North"
case West => "West"
case South => "South"
def rotLeft: Dir = rotationOrder(rotationOrder.indexOf(dir) + 1)
def rotRight: Dir = rotationOrder(rotationOrder.lastIndexOf(dir) - 1)
def flip: Dir = rotationOrder(rotationOrder.indexOf(dir) + 2)
def flipHor: Dir = dir match
case West => East
case East => West
case o => o
def flipVert: Dir = dir match
case North => South
case South => North
case o => o
given Show[Dir] with
def show(d: Dir): String = d.str