Skip to content

Commit

Permalink
2023-17
Browse files Browse the repository at this point in the history
  • Loading branch information
jurisk committed Dec 17, 2023
1 parent 0d63dd5 commit 6eb1429
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 68 deletions.
114 changes: 47 additions & 67 deletions scala2/src/main/scala/jurisk/adventofcode/y2023/Advent17.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand All @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion scala2/src/main/scala/jurisk/geometry/Rotation.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package jurisk.geometry

sealed trait Rotation {
sealed trait Rotation extends Product with Serializable {
def inverse: Rotation
}

Expand Down

0 comments on commit 6eb1429

Please sign in to comment.