Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions 2015/src/main/scala/aoc2015/Day03.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ object Day03 extends AoC:

infix def next(cmd: Area.Command): Area =
cmd match
case '>' => moveToAndDeliver(start.copy(x = start.x + 1))
case '<' => moveToAndDeliver(start.copy(x = start.x - 1))
case '^' => moveToAndDeliver(start.copy(y = start.y + 1))
case 'v' => moveToAndDeliver(start.copy(y = start.y - 1))
case '>' => moveToAndDeliver((x = start.x + 1, y = start.y))
case '<' => moveToAndDeliver((x = start.x - 1, y = start.y))
case '^' => moveToAndDeliver((x = start.x, y = start.y + 1))
case 'v' => moveToAndDeliver((x = start.x, y = start.y - 1))

object Area:

Expand Down
107 changes: 13 additions & 94 deletions 2015/src/main/scala/aoc2015/Day09.scala
Original file line number Diff line number Diff line change
@@ -1,112 +1,31 @@
package aoc2015

import nmcb.*
import scala.annotation.*

object Day09 extends AoC:

type Node = String
type Edge = (from: Node, to: Node, distance: Int)

final case class Edge(from: Node, to: Node, distance: Int)
val edges: Vector[Edge] =
lines
.flatMap:
case s"""$f to $t = $d""" => Vector((f, t, d.toInt), (t, f, d.toInt))

final case class Graph(neighbours: Map[Node, List[Edge]] = Map.empty):
def add(edge: Edge): Graph =
val reverse: Edge = Edge(edge.to, edge.from, edge.distance)
Graph(
neighbours
.updated(edge.from, neighbours.getOrElse(edge.from, List.empty) :+ edge)
.updated(reverse.from, neighbours.getOrElse(reverse.from, List.empty) :+ reverse)
)

object Graph:
def empty: Graph =
Graph(Map.empty)

object Dijkstra {

import scala.collection.mutable

type Dist = (Node, Int)

val sortByDistance: Ordering[Dist] =
(d1, d2) => d1._2.compareTo(d2._2)

def run(graph: Graph, from: Node): Result =

val edgeTo: mutable.Map[Node, Edge] =
mutable.Map.empty

val distTo: mutable.Map[Node, Int] =
mutable.Map.from(graph.neighbours.map((node,_) => node -> Int.MaxValue))

// init source distance and add to the queue
distTo += from -> 0
val sourceEdge = from -> distTo(from)
val queue = mutable.PriorityQueue[(Node, Int)](sourceEdge)(using sortByDistance)

while (queue.nonEmpty)
val (minDistNode, _) = queue.dequeue()
val edges = graph.neighbours.getOrElse(minDistNode, List.empty)

edges.foreach(edge =>

if distTo(edge.to) > distTo(edge.from) + edge.distance then {
distTo.update(edge.to, distTo(edge.from) + edge.distance)
edgeTo.update(edge.to, edge)
if !queue.exists((node,_) => node == edge.to) then
queue.enqueue((edge.to, distTo(edge.to)))
}
)

Result(edgeTo.toMap, distTo.toMap)
}

case class Result(edgeTo: Map[Node,Edge], distancesTo: Map[Node,Int]):

def pathTo(node: Node): Either[Node, Seq[Edge]] =

@tailrec def go(edges: List[Edge], node: Node): List[Edge] =
edgeTo.get(node) match
case Some(edge) => go(edge +: edges, edge.from)
case None => edges

hasEdge(node).map(b => if (!b) Seq.empty else go(List.empty, node))


def hasEdge(node: Node): Either[Node, Boolean] =
distancesTo.get(node).map(_ < Int.MaxValue).toRight(s"node=$node does not exist")

def distanceTo(node: Node): Either[Node, Int] =
distancesTo.get(node).toRight(s"node=$node does not exist")

/** Input */

val edges: List[Edge] =
def parser(s: String): List[Edge] =
s match
case s"""$from to $to = $distance""" => List(Edge(from, to, distance.toInt), Edge(to, from, distance.toInt))
case line: String => sys.error(s"could not parse '$line'")

lines.flatMap(parser).toList

// FFS you have ALL distances between ALL pairs of cities !!! ie
// you DON'T need Dijkstra to solve THIS issue omg, omg, OMG

def distance(nodes: List[Node]): Int =
assert(nodes.size == 2)
def distance(edges: Vector[Edge], from: Node, to: Node): Int =
edges
.find(e => e.from == nodes(0) && e.to == nodes(1))
.find(e => e.from == from && e.to == to)
.map(_.distance)
.getOrElse(sys.error("no route"))
.getOrElse(sys.error(s"no route from $from to $to"))

lazy val answer: List[Int] =
def solve(edges: Vector[Edge]): Vector[Int] =
edges
.map(_.from)
.distinct
.permutations
.map(_.sliding(2).foldLeft(0)((acc,route) => acc + distance(route)))
.toList
.map(_.sliding(2).foldLeft(0)((result, route) => result + distance(edges, route(0), route(1))))
.toVector


lazy val answer1: Int = answer.min
lazy val answer2: Int = answer.max
lazy val answer1: Int = solve(edges).min
lazy val answer2: Int = solve(edges).max
12 changes: 6 additions & 6 deletions 2016/src/main/scala/aoc2016/Day17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,20 @@ object Day17 extends AoC:

def move(direction: Char): Pos =
direction match
case 'U' => p.copy(y = p.y - 1)
case 'D' => p.copy(y = p.y + 1)
case 'L' => p.copy(x = p.x - 1)
case 'R' => p.copy(x = p.x + 1)
case 'U' => (x = p.x, y = p.y - 1)
case 'D' => (x = p.x, y = p.y + 1)
case 'L' => (x = p.x - 1, y = p.y)
case 'R' => (x = p.x + 1, y = p.y)


/** hardcoded for 4x4 grid */
case class Path(passcode: String, path: String = "", target: Pos = Pos(0, 0)):
case class Path(passcode: String, path: String = "", target: Pos = Pos.of(0, 0)):

def withinGrid: Boolean =
target.x >= 0 && target.x < 4 && target.y >= 0 && target.y < 4

def reachedVault: Boolean =
target == Pos(3, 3)
target == Pos.of(3, 3)

def openDoors: String =
md5
Expand Down
12 changes: 6 additions & 6 deletions 2016/src/main/scala/aoc2016/Day22.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object Day22 extends AoC:
.filter(_.startsWith("/dev/grid/node"))
.map: line =>
val Array(x, y, _, used, avail, _) = line.split("\\D+").tail.map(_.toInt)
Pos(x, y) -> Node(used, avail)
Pos.of(x, y) -> Node(used, avail)
.toMap

def viable(nodes: Map[Pos,Node]): Vector[(Node,Node)] =
Expand All @@ -46,10 +46,10 @@ object Day22 extends AoC:
y <- 0 to maxY
x <- 0 to maxX
do
val pos = Pos(x, y)
val pos = Pos.of(x, y)
val node = nodes(pos)
if pos == Pos(0, 0) then sb.append('T')
else if pos == Pos(maxX, 0) then sb.append('S')
if pos == Pos.of(0, 0) then sb.append('T')
else if pos == Pos.of(maxX, 0) then sb.append('S')
else sb.append(node.toChar)
if x == maxX then sb.append('\n')
sb.append('\n').toString
Expand Down Expand Up @@ -104,8 +104,8 @@ object Day22 extends AoC:
*
*/

val source: Pos = Pos(nodes.maxX, 0)
val target: Pos = Pos(0, 0)
val source: Pos = Pos.of(nodes.maxX, 0)
val target: Pos = Pos.of(0, 0)
val empty: Pos = nodes.find(_.node.isEmpty).map(_.pos).get
val massive: Vector[Pos] = nodes.filter(_.node.isMassive).map(_.pos).toVector

Expand Down
4 changes: 2 additions & 2 deletions 2016/src/main/scala/aoc2016/Day24.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ object Day24 extends AoC:
type Routes = Vector[Vector[Int]]

val(grid: Grid, nodes: Nodes) =
val grid = for y <- lines.indices ; x <- lines.head.indices if lines(y)(x) != '#' yield Pos(x, y)
val nodes = for y <- lines.indices ; x <- lines.head.indices if lines(y)(x).isDigit yield lines(y)(x).asDigit -> Pos(x, y)
val grid = for y <- lines.indices ; x <- lines.head.indices if lines(y)(x) != '#' yield Pos.of(x, y)
val nodes = for y <- lines.indices ; x <- lines.head.indices if lines(y)(x).isDigit yield lines(y)(x).asDigit -> Pos.of(x, y)
(grid.toSet, nodes.toMap)

/** breath first search */
Expand Down
14 changes: 7 additions & 7 deletions 2017/src/main/scala/aoc2017/Day03.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ object Day03 extends AoC:
val quadrant = base / size
val offset = base % size
quadrant match
case 0 => Pos(half, offset + 1 - half)
case 1 => Pos(half - 1 - offset, half)
case 2 => Pos(-half, half - 1 - offset)
case 3 => Pos(offset + 1 - half, -half)
case 0 => Pos.of(half, offset + 1 - half)
case 1 => Pos.of(half - 1 - offset, half)
case 2 => Pos.of(-half, half - 1 - offset)
case 3 => Pos.of(offset + 1 - half, -half)

def spiralSquares(to: Int):Int =

Expand All @@ -29,10 +29,10 @@ object Day03 extends AoC:
x <- -1 to 1
if !(x == 0 && y == 0)
yield
Pos(x, y)
Pos.of(x, y)

@tailrec
def loop(n: Int, squares: Map[Pos,Int] = Map(Pos(0,0) -> 1)): Int =
def loop(n: Int, squares: Map[Pos,Int] = Map(Pos.of(0,0) -> 1)): Int =
val point = position(n)
val result = spiralScan.map(_ + point).flatMap(squares.get).sum
if result > to then
Expand All @@ -41,5 +41,5 @@ object Day03 extends AoC:
loop(n + 1, squares.updated(point, result))
loop(2)

lazy val answer1: Int = position(277678).manhattan(Pos.origin).toInt
lazy val answer1: Int = position(277678).manhattanDistance(Pos.origin).toInt
lazy val answer2: Int = spiralSquares(277678)
2 changes: 1 addition & 1 deletion 2017/src/main/scala/aoc2017/Day14.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object Day14 extends AoC:
case (acc,(row, y)) =>
row.foldLeft(acc):
case (acc, (square, x)) =>
if square == '1' then acc + Pos(x,y) else acc
if square == '1' then acc + Pos.of(x,y) else acc

def regions(used: Set[Pos]): Set[Set[Pos]] =
val graph = used.map(square => square -> square.adjacent.intersect(used)).toMap
Expand Down
4 changes: 2 additions & 2 deletions 2017/src/main/scala/aoc2017/Day19.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ object Day19 extends AoC:
extension (g: Grid)

def start: Pos =
Pos(g.head.indexOf('|'), 0)
Pos.of(g.head.indexOf('|'), 0)

def charAt(p: Pos): Char =
grid.lift(p.y).flatMap(_.lift(p.x)).getOrElse(' ')
Expand All @@ -38,7 +38,7 @@ object Day19 extends AoC:

object Tracer:
def start(grid: Grid): Tracer =
Tracer(grid, grid.start, Pos(0,1))
Tracer(grid, grid.start, Pos.of(0,1))

val tracer: Tracer = Tracer.start(grid)
val path: Seq[Tracer] = Iterator.iterate(tracer)(_.next).takeWhile(_.isPath).toList
Expand Down
6 changes: 3 additions & 3 deletions 2017/src/main/scala/aoc2017/Day22.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ object Day22 extends AoC:
private val carrier: Carrier =

val current: Pos =
Pos(lines(0).length / 2, lines.length / 2)
Pos.of(lines(0).length / 2, lines.length / 2)

val nodes: Map[Pos,Status] =
lines
Expand All @@ -75,8 +75,8 @@ object Day22 extends AoC:
row
.zipWithIndex
.map:
case ('#',x) => Pos(x,y) -> Infected
case ('.',x) => Pos(x,y) -> Clean
case ('#',x) => Pos.of(x,y) -> Infected
case ('.',x) => Pos.of(x,y) -> Clean
case ( c ,_) => sys.error(s"unexpected char=$c")
.toMap
.withDefaultValue(Clean)
Expand Down
8 changes: 4 additions & 4 deletions 2018/src/main/scala/aoc2018/Day06.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ object Day06 extends AoC:
val maxY: Int = coordinates.maxBy(_.y).y

val positions: Vector[Pos] =
(for x <- minX to maxX ; y <- minY to maxY yield Pos(x,y)).toVector
(for x <- minX to maxX ; y <- minY to maxY yield Pos.of(x,y)).toVector

type UnitDistance = (Pos, Long)

Expand All @@ -28,7 +28,7 @@ object Day06 extends AoC:

val closest: Vector[(Pos,Pos)] =
positions.flatMap: p =>
coordinates.map(c => (c, c manhattan p))
coordinates.map(c => (c, c.manhattanDistance(p)))
.sortBy(_.distance).take(2) match
case a +: b +: _ if a.distance == b.distance => None
case a +: _ => Some(p,a.coordinate)
Expand All @@ -45,14 +45,14 @@ object Day06 extends AoC:
size

def manhattanSum(position: Pos): Long =
coordinates.map(position.manhattan).sum
coordinates.map(position.manhattanDistance).sum

def withinManhattanSumLimit(limit: Int): Vector[Pos] =
positions.filter(p => manhattanSum(p) < limit)


val coordinates: Vector[Pos] = lines.map:
case s"$x, $y" => Pos(x.toInt, y.toInt)
case s"$x, $y" => Pos.of(x.toInt, y.toInt)

lazy val answer1: Long = Grid(coordinates).largestAreaSize
lazy val answer2: Int = Grid(coordinates).withinManhattanSumLimit(10000).size
4 changes: 2 additions & 2 deletions 2018/src/main/scala/aoc2018/Day10.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object Day10 extends AoC:
def fromString(s: String): Point =
s.trim match
case s"position=<$x, $y> velocity=<$vx, $vy>" =>
Point(Pos(x.trim.toInt, y.trim.toInt), Pos(vx.trim.toInt, vy.trim.toInt))
Point(Pos.of(x.trim.toInt, y.trim.toInt), Pos.of(vx.trim.toInt, vy.trim.toInt))


// returns the range containing the first domain which yields at least `x` as its codomain, starting from `min`.
Expand Down Expand Up @@ -77,7 +77,7 @@ object Day10 extends AoC:
val positionsSet = positions.toSet

(min.y to max.y).foldLeft(StringBuffer())((sb,y) => (min.x to max.x).foldLeft(sb)((sb,x) =>
if positionsSet.contains(Pos(x,y)) then sb.append('#') else sb.append('.')
if positionsSet.contains(Pos.of(x,y)) then sb.append('#') else sb.append('.')
).append('\n')).toString

val (sky, fastest) =
Expand Down
2 changes: 1 addition & 1 deletion 2018/src/main/scala/aoc2018/Day13.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package aoc2018

import nmcb.*
import nmcb.pos.*
import nmcb.pos.{*, given}

import scala.annotation.tailrec

Expand Down
6 changes: 3 additions & 3 deletions 2018/src/main/scala/aoc2018/Day15.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ object Day15 extends AoC:

case class Fighter(fighterType: FighterType, pos: Pos, hitPoints: Int = 200, attackPower: Int = 3)

given posReadingOrdering: Ordering[Pos] = Ordering.by(p => (p.y, p.x))
given combatUnitReadingOrdering: Ordering[Fighter] = Ordering.by(_.pos)
given Ordering[Pos] = Ordering.by(_.toTuple.swap)
given Ordering[Fighter] = Ordering.by(_.pos)

def targetsOf(fighter: Fighter)(using fighters: List[Fighter]): Set[Fighter] =
fighters.filter(_.fighterType == fighter.fighterType.target).toSet
Expand Down Expand Up @@ -182,7 +182,7 @@ object Day15 extends AoC:
for
(row,y) <- parseGrid(input).view.zipWithIndex.toList
(tile,x) <- row.view.zipWithIndex
fighter <- parseFighter(tile, Pos(x,y))
fighter <- parseFighter(tile, Pos.of(x,y))
yield fighter

val grid: Grid =
Expand Down
Loading