From 6eb1429c8f06025a8abd9fe65c6d4e283f953595 Mon Sep 17 00:00:00 2001 From: Juris Date: Sun, 17 Dec 2023 07:47:12 +0200 Subject: [PATCH] 2023-17 --- .../jurisk/adventofcode/y2023/Advent17.scala | 114 ++++++++---------- .../main/scala/jurisk/geometry/Rotation.scala | 2 +- 2 files changed, 48 insertions(+), 68 deletions(-) diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent17.scala b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent17.scala index 27a2f77f..3edf4299 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent17.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent17.scala @@ -4,8 +4,9 @@ import cats.implicits.{catsSyntaxOptionId, none} import jurisk.algorithms.pathfinding.Dijkstra import jurisk.geometry.Direction2D.{CardinalDirection2D, E, S} import jurisk.geometry.Rotation.{Left90, NoRotation, Right90} -import jurisk.geometry.{Coords2D, Direction2D, Field2D} +import jurisk.geometry.{Coords2D, Field2D} import jurisk.utils.FileInput._ +import jurisk.utils.Parsing.StringOps object Advent17 { type Input = Field2D[Int] @@ -16,22 +17,28 @@ object Advent17 { final case class State( coords: Coords2D, direction: Option[CardinalDirection2D], - singleDirection: Int, + singleDirectionCounter: Int, ) - def successors1(data: Input, state: State): List[(State, Int)] = { + private def successors( + data: Input, + atMostInSingleDirection: Int, + minimumBeforeTurning: Int, + )(state: State): List[(State, Int)] = { val candidateDirections = state.direction match { case Some(lastDirection) => - if (state.singleDirection == 3) { - // must turn - List(Left90, Right90) map { rotation => - lastDirection.rotate(rotation) - } - } else { - List(Left90, NoRotation, Right90) map { rotation => - lastDirection.rotate(rotation) - } - } + val turns = + if (state.singleDirectionCounter >= minimumBeforeTurning) { + // Allowed to turn + List(Left90, Right90) + } else Nil + val straights = + if (state.singleDirectionCounter < atMostInSingleDirection) { + // Allowed to go straight + List(NoRotation) + } else Nil + + (turns ::: straights) map lastDirection.rotate case None => // Start @@ -40,77 +47,50 @@ object Advent17 { candidateDirections flatMap { direction => val nextCoords = state.coords + direction - data.at(nextCoords) map { nextValue => - State( + data.at(nextCoords) map { heatLoss => + val newState = State( nextCoords, direction.some, if (direction.some == state.direction) { - state.singleDirection + 1 + state.singleDirectionCounter + 1 } else { 1 }, - ) -> nextValue + ) + newState -> heatLoss } } } - def successors2(data: Input, state: State): List[(State, Int)] = { - val candidateDirections = state.direction match { - case Some(lastDirection) => - if (state.singleDirection == 10) { - // must turn - List(Left90, Right90) map { rotation => - lastDirection.rotate(rotation) - } - } else if (state.singleDirection < 4) { - // cannot turn yet - lastDirection.rotate(NoRotation) :: Nil - } else { - List(Left90, NoRotation, Right90) map { rotation => - lastDirection.rotate(rotation) - } - } - - case None => - // Start - E :: S :: Nil - } + private def solve( + data: Input, + atMostInSingleDirection: Int, + minimumBeforeStoppingOrTurning: Int, + ): Int = { + val calculateSuccessors = successors( + data, + atMostInSingleDirection, + minimumBeforeStoppingOrTurning, + ) _ - candidateDirections flatMap { direction => - val nextCoords = state.coords + direction - data.at(nextCoords) map { nextValue => - State( - nextCoords, - direction.some, - if (direction.some == state.direction) { - state.singleDirection + 1 - } else { - 1 - }, - ) -> nextValue - } - } - } - - def part1(data: Input): Int = { val result = Dijkstra.dijkstra[State, Int]( - State(coords = data.topLeft, none, singleDirection = 0), - x => successors1(data, x), - _.coords == data.bottomRight, + State(coords = data.topLeft, none, singleDirectionCounter = 0), + calculateSuccessors, + s => + s.coords == data.bottomRight && s.singleDirectionCounter >= minimumBeforeStoppingOrTurning, ) - result.get._2 + result match { + case Some((_, result)) => result + case None => "Failed to solve".fail + } } - def part2(data: Input): Int = { - val result = Dijkstra.dijkstra[State, Int]( - State(coords = data.topLeft, none, singleDirection = 0), - x => successors2(data, x), - s => s.coords == data.bottomRight && s.singleDirection >= 4, - ) + def part1(data: Input): Int = + solve(data, 3, 0) - result.get._2 - } + def part2(data: Input): Int = + solve(data, 10, 4) def parseFile(fileName: String): Input = parse(readFileText(fileName)) diff --git a/scala2/src/main/scala/jurisk/geometry/Rotation.scala b/scala2/src/main/scala/jurisk/geometry/Rotation.scala index 60af406c..6af16ad8 100644 --- a/scala2/src/main/scala/jurisk/geometry/Rotation.scala +++ b/scala2/src/main/scala/jurisk/geometry/Rotation.scala @@ -1,6 +1,6 @@ package jurisk.geometry -sealed trait Rotation { +sealed trait Rotation extends Product with Serializable { def inverse: Rotation }