103 lines
3.5 KiB
Scala
103 lines
3.5 KiB
Scala
package aoc.y2019
|
|
|
|
import java.util.UUID
|
|
|
|
import aoc.Day
|
|
import cats._
|
|
import cats.implicits._
|
|
import cats.data.State
|
|
|
|
object Day03 extends Day {
|
|
|
|
sealed trait Direction
|
|
case object Up extends Direction
|
|
case object Down extends Direction
|
|
case object Left extends Direction
|
|
case object Right extends Direction
|
|
case class Move(dir: Direction, spaces: Int) {
|
|
|
|
val directions = Vector.fill(spaces)(dir)
|
|
|
|
}
|
|
|
|
case class Wire(moves: Vector[Move])
|
|
|
|
import fastparse._, SingleLineWhitespace._
|
|
|
|
def parseUp[_: P]: P[Up.type] = P("U").map(_ => Up)
|
|
def parseDown[_: P]: P[Down.type] = P("D").map(_ => Down)
|
|
def parseLeft[_: P]: P[Left.type] = P("L").map(_ => Left)
|
|
def parseRight[_: P]: P[Right.type] = P("R").map(_ => Right)
|
|
def parseDirection[_: P]: P[Direction] = parseUp | parseDown | parseLeft | parseRight
|
|
|
|
def parseNumber[_: P]: P[Int] = P(CharIn("0-9").rep(1).!).map(_.toInt)
|
|
|
|
def parseMove[_: P]: P[Move] = P(parseDirection ~ parseNumber).map {
|
|
case (direction, spaces) => Move(direction, spaces)
|
|
}
|
|
|
|
def parseLine[_: P]: P[Vector[Move]] = P(parseMove.rep(1, ",")).map(_.toVector)
|
|
|
|
case class Coordinate(x: Int, y: Int) {
|
|
|
|
def move(direction: Direction, times: Int = 1): Coordinate = direction match {
|
|
case Up => Coordinate(x, y + times)
|
|
case Down => Coordinate(x, y - times)
|
|
case Left => Coordinate(x - times, y)
|
|
case Right => Coordinate(x + times, y)
|
|
}
|
|
|
|
def manhattanDistance: Int = Math.abs(x) + Math.abs(y)
|
|
|
|
}
|
|
|
|
val emptyBoard: Board = (Vector(), Vector())
|
|
type Board = (Vector[Coordinate], Vector[Coordinate])
|
|
type BoardModification = State[Board, Unit]
|
|
|
|
def lay(wires: (Wire, Wire)): BoardModification = {
|
|
wires match {
|
|
case (Wire(leftWireMoves), Wire(rightWireMoves)) =>
|
|
val leftWireDirections = leftWireMoves.flatMap(_.directions)
|
|
val rightWireDirections = rightWireMoves.flatMap(_.directions)
|
|
val leftWireOptions = leftWireDirections.map(Some(_)).padTo(rightWireDirections.length, None)
|
|
val rightWireOptions = rightWireDirections.map(Some(_)).padTo(leftWireDirections.length, None)
|
|
val moves = leftWireOptions.zip(rightWireOptions)
|
|
moves.traverse(move).map(_ => ())
|
|
}
|
|
}
|
|
|
|
def move(move: (Option[Direction], Option[Direction])): BoardModification = State.modify {
|
|
case (leftWire, rightWire) => move match {
|
|
case (leftMove, rightMove) =>
|
|
(
|
|
leftMove.fold(leftWire)(d => leftWire.appended(leftWire.lastOption.getOrElse(Coordinate(0, 0)).move(d))),
|
|
rightMove.fold(rightWire)(d => rightWire.appended(rightWire.lastOption.getOrElse(Coordinate(0, 0)).move(d)))
|
|
)
|
|
}
|
|
}
|
|
|
|
def endingBoard(input: String): (Vector[Coordinate], Vector[Coordinate]) = {
|
|
val Vector(wireOneStr, wireTwoStr) = input.linesIterator.toVector
|
|
val wireOne = Wire(parse(wireOneStr, parseLine(_)).get.value)
|
|
val wireTwo = Wire(parse(wireTwoStr, parseLine(_)).get.value)
|
|
val layWires = lay(wireOne, wireTwo)
|
|
val endingBoard: Board = layWires.runS(emptyBoard).value
|
|
endingBoard
|
|
}
|
|
|
|
override def part1(input: String): String = {
|
|
val (leftCoords, rightCoords) = endingBoard(input)
|
|
val intersections = leftCoords.intersect(rightCoords)
|
|
intersections.map(_.manhattanDistance).min.toString
|
|
}
|
|
|
|
override def part2(input: String): String = {
|
|
val (leftCoords, rightCoords) = endingBoard(input)
|
|
val intersections = leftCoords.intersect(rightCoords)
|
|
val intersectionSteps = intersections.map(c => leftCoords.indexOf(c) + rightCoords.indexOf(c) + 2)
|
|
intersectionSteps.min.toString
|
|
}
|
|
|
|
}
|