From e19eb3ff262e9db030acfaeea87bd9374cf90d90 Mon Sep 17 00:00:00 2001 From: Juris Date: Tue, 19 Dec 2023 06:19:18 +0200 Subject: [PATCH] Refactoring --- .../jurisk/adventofcode/y2018/Advent13.scala | 6 +- .../jurisk/adventofcode/y2023/Advent16.scala | 67 ++++++++++--------- .../geometry/CoordsAndDirection2D.scala | 2 + .../adventofcode/y2023/Advent16Spec.scala | 4 +- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent13.scala b/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent13.scala index 354e9c0c..7790c039 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent13.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2018/Advent13.scala @@ -24,8 +24,7 @@ object Advent13 { private val rotations = List(Left90, NoRotation, Right90) - def coords: Coords2D = at.coords - def direction: CardinalDirection2D = at.direction + def coords: Coords2D = at.coords def directionSymbol: Char = at.direction match { case N => '^' @@ -36,7 +35,8 @@ object Advent13 { } def next(board: Field2D[Track]): Cart = { - val square = board.atOrElse(coords, Track.Empty) + val square = board.atOrElse(coords, Track.Empty) + val direction = at.direction val newDirection = square match { case Track.Intersection => diff --git a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala index c42e33a5..f63e3f6a 100644 --- a/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala +++ b/scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala @@ -6,6 +6,7 @@ import jurisk.adventofcode.y2023.Advent16.Square.Empty import jurisk.collections.BiMap import jurisk.collections.BiMap.BiDirectionalArrowAssociation import jurisk.geometry.Coords2D +import jurisk.geometry.CoordsAndDirection2D import jurisk.geometry.Direction2D import jurisk.geometry.Direction2D._ import jurisk.geometry.Field2D @@ -82,22 +83,25 @@ object Advent16 { ) final case class State( - incomingQueue: Set[(Coords2D, CardinalDirection2D)], - outgoingQueue: Set[(Coords2D, CardinalDirection2D)], - incomingEdgesProcessed: Set[(Coords2D, CardinalDirection2D)], + incomingQueue: Set[CoordsAndDirection2D], + outgoingQueue: Set[CoordsAndDirection2D], + incomingEdgesProcessed: Set[CoordsAndDirection2D], ) { def next(field: Input): State = { - val newIncomingQueue = outgoingQueue.flatMap { case (c, dir) => - val neighbourCoords = c + dir - field.isValidCoordinate(neighbourCoords) option { - neighbourCoords -> dir.invert + val newIncomingQueue = outgoingQueue.flatMap { next => + val neighbour = next.nextStraight + field.isValidCoordinate(neighbour.coords) option { + neighbour.invertDirection } } -- incomingEdgesProcessed val newOutgoingQueue = for { - (c, dir) <- incomingQueue - outgoing <- field.at(c).toList.flatMap(_.incomingToOutgoing(dir)) - } yield c -> outgoing + next <- incomingQueue + outgoing <- field + .at(next.coords) + .toList + .flatMap(_.incomingToOutgoing(next.direction)) + } yield next.copy(direction = outgoing) State( newIncomingQueue, @@ -109,10 +113,9 @@ object Advent16 { object State { def fromInitial( - initialSquare: Coords2D, - initialDirection: CardinalDirection2D, + initial: CoordsAndDirection2D ): State = { - val incomingQueue = Set(initialSquare -> initialDirection) + val incomingQueue = Set(initial) State( incomingQueue = incomingQueue, outgoingQueue = Set.empty, @@ -123,12 +126,11 @@ object Advent16 { private def solveBySimulation( field: Input, - initialSquare: Coords2D, - initialDirection: CardinalDirection2D, + initial: CoordsAndDirection2D, ): Long = { - val initial = State.fromInitial(initialSquare, initialDirection) - val endState = Simulation.runUntilStableState(initial)(_.next(field)) - endState.incomingEdgesProcessed.map { case (c, _) => c }.size + val initialState = State.fromInitial(initial) + val endState = Simulation.runUntilStableState(initialState)(_.next(field)) + endState.incomingEdgesProcessed.map(_.coords).size } sealed trait MinimizeOrMaximize @@ -139,7 +141,7 @@ object Advent16 { private[y2023] def solveByOptimization( field: Input, - initial: Option[(Coords2D, CardinalDirection2D)], + initial: Option[CoordsAndDirection2D], optimizationDirection: MinimizeOrMaximize, debug: Boolean = false, ): Long = { @@ -202,8 +204,8 @@ object Advent16 { } // Exactly one of the incomings from the edge is 1 - val allEdgeIncomings = edgeIncomings(field).map { case (c, d) => - incomingBool(c, d).toInt + val allEdgeIncomings = edgeIncomings(field).map { edge => + incomingBool(edge.coords, edge.direction).toInt }.toSeq assert(allEdgeIncomings.distinct.length == (field.height + field.width) * 2) @@ -213,9 +215,9 @@ object Advent16 { ) // Only for Part 1 - initialSquare incoming initialDirection is 1, others are 0 - initial foreach { case (initialSquare, initialDirection) => + initial foreach { initial => addConstraints( - incomingBool(initialSquare, initialDirection) === True + incomingBool(initial.coords, initial.direction) === True ) } @@ -291,26 +293,25 @@ object Advent16 { private def edgeIncomings( field: Input - ): Iterable[(Coords2D, CardinalDirection2D)] = - field.topRowCoords.map(_ -> N) ::: - field.rightColumnCoords.map(_ -> E) ::: - field.bottomRowCoords.map(_ -> S) ::: - field.leftColumnCoords.map(_ -> W) + ): Iterable[CoordsAndDirection2D] = + field.topRowCoords.map(CoordsAndDirection2D(_, N)) ::: + field.rightColumnCoords.map(CoordsAndDirection2D(_, E)) ::: + field.bottomRowCoords.map(CoordsAndDirection2D(_, S)) ::: + field.leftColumnCoords.map(CoordsAndDirection2D(_, W)) private[y2023] def part1Simulation(field: Input): Long = - solveBySimulation(field, Coords2D.Zero, Direction2D.W) + solveBySimulation(field, CoordsAndDirection2D(Coords2D.Zero, Direction2D.W)) private[y2023] def part1Optimization(field: Input): Long = solveByOptimization( field, - (Coords2D.Zero, Direction2D.W).some, + CoordsAndDirection2D(Coords2D.Zero, Direction2D.W).some, MinimizeOrMaximize.Minimize, ) private[y2023] def part2Simulation(field: Input): Long = { - val solutions = edgeIncomings(field) map { - case (initialSquare, initialDirection) => - solveBySimulation(field, initialSquare, initialDirection) + val solutions = edgeIncomings(field) map { initial => + solveBySimulation(field, initial) } solutions.max diff --git a/scala2/src/main/scala/jurisk/geometry/CoordsAndDirection2D.scala b/scala2/src/main/scala/jurisk/geometry/CoordsAndDirection2D.scala index 02f0d93b..56bd009a 100644 --- a/scala2/src/main/scala/jurisk/geometry/CoordsAndDirection2D.scala +++ b/scala2/src/main/scala/jurisk/geometry/CoordsAndDirection2D.scala @@ -14,4 +14,6 @@ final case class CoordsAndDirection2D( coords + newDirection, newDirection, ) + + def invertDirection: CoordsAndDirection2D = copy(direction = direction.invert) } diff --git a/scala2/src/test/scala/jurisk/adventofcode/y2023/Advent16Spec.scala b/scala2/src/test/scala/jurisk/adventofcode/y2023/Advent16Spec.scala index 4283382b..d329d0b7 100644 --- a/scala2/src/test/scala/jurisk/adventofcode/y2023/Advent16Spec.scala +++ b/scala2/src/test/scala/jurisk/adventofcode/y2023/Advent16Spec.scala @@ -3,7 +3,7 @@ package jurisk.adventofcode.y2023 import org.scalatest.freespec.AnyFreeSpec import Advent16._ import cats.implicits._ -import jurisk.geometry.{Coords2D, Direction2D} +import jurisk.geometry.{Coords2D, CoordsAndDirection2D, Direction2D} import org.scalatest.matchers.should.Matchers._ class Advent16Spec extends AnyFreeSpec { @@ -18,7 +18,7 @@ class Advent16Spec extends AnyFreeSpec { solveByOptimization( test, - (Coords2D.Zero, Direction2D.W).some, + CoordsAndDirection2D(Coords2D.Zero, Direction2D.W).some, MinimizeOrMaximize.Maximize, debug = true, ) shouldEqual 1