aoc-old/aoc/src/main/scala/aoc/y2016/Day01.scala

112 lines
3.0 KiB
Scala

package aoc.y2016
import aoc._
object Day01 extends Day {
import fastparse._, SingleLineWhitespace._
sealed trait Direction {
def score: (Int, Int)
}
case object MoveUp extends Direction {
override def score: (Int, Int) = (0, 1)
}
case object MoveDown extends Direction {
override def score: (Int, Int) = (0, -1)
}
case object MoveLeft extends Direction {
override def score: (Int, Int) = (-1, 0)
}
case object MoveRight extends Direction {
override def score: (Int, Int) = (1, 0)
}
case class State(currentDirection: Direction = MoveUp, distance: (Int, Int) = (0, 0))
sealed trait Turn {
def apply(d: Direction): Direction
}
case object TurnLeft extends Turn {
override def apply(d: Direction): Direction = d match {
case MoveUp => MoveLeft
case MoveLeft => MoveDown
case MoveDown => MoveRight
case MoveRight => MoveUp
}
}
case object TurnRight extends Turn {
override def apply(d: Direction): Direction = d match {
case MoveUp => MoveRight
case MoveRight => MoveDown
case MoveDown => MoveLeft
case MoveLeft => MoveUp
}
}
case class Move(t: Turn, l: Int)
val tMap = Map(
"L" -> TurnLeft,
"R" -> TurnRight
)
def timesParser[_: P]: P[Int] = P(CharIn("0-9").rep(1).!).map(_.toInt)
def directionParser[_: P]: P[Turn] = P(("L" | "R").!).map(tMap)
def moveParser[_: P]: P[Move] = P(directionParser ~ timesParser).map { case (t, l) => Move(t, l) }
def movesParser[_: P]: P[List[Move]] = P(moveParser.rep(1, ","./)).map(_.toList)
def getMoves(input: String): List[Move] = {
val Parsed.Success(moves, _) = parse(input, movesParser(_))
moves
}
def distance(s: State): Int = {
val (x, y) = s.distance
val (dx, dy) = (Math.abs(x), Math.abs(y))
dx + dy
}
override def part1(input: String): String = {
val moves = getMoves(input)
val endingState = moves.foldLeft(State())((s, m) => {
val nd = m.t(s.currentDirection)
val ns = nd.score * m.l
State(nd, s.distance + ns)
})
distance(endingState).toString
}
override def part2(input: String): String = {
val moves = getMoves(input)
def firstRepeat(
m: List[Move],
startingState: State = State(),
carryOutRotate: Boolean = true,
seen: List[(Int, Int)] = List()
): State = {
val thisMove = m.head
val nd = if (carryOutRotate) {
thisMove.t(startingState.currentDirection)
} else {
startingState.currentDirection
}
val ns = startingState.distance + nd.score
print(s"\r(${ns._1}, ${ns._2}) ")
val ss = State(nd, ns)
if (seen.contains(ns)) {
ss
} else {
val xw = thisMove.l == 1
val nm = if (xw) {
m.drop(1)
} else {
Move(thisMove.t, thisMove.l - 1) +: m.drop(1)
}
firstRepeat(nm, ss, xw, seen :+ ns)
}
}
val rs = firstRepeat(moves)
println()
distance(rs).toString
}
}