Skip to content

Commit

Permalink
2023-17 (#13)
Browse files Browse the repository at this point in the history
* 2023-17

* 2023-17

* 2023-17

* 2023-17

* 2023-17

---------

Co-authored-by: Juris <[email protected]>
  • Loading branch information
jurisk and jurisk authored Dec 17, 2023
1 parent 8a3c3cb commit cda4dc7
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 13 deletions.
1 change: 1 addition & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
| 2023-14 | [Parabolic Reflector Dish](https://adventofcode.com/2023/day/14) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2023/Advent14.scala) | | |
| 2023-15 | [Lens Library](https://adventofcode.com/2023/day/15) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2023/Advent15.scala) | | |
| 2023-16 | [The Floor Will Be Lava](https://adventofcode.com/2023/day/16) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2023/Advent16.scala) | | |
| 2023-17 | [Clumsy Crucible](https://adventofcode.com/2023/day/17) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2023/Advent17.scala) | | |
| 2022-01 | [Calorie Counting](https://adventofcode.com/2022/day/1) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2022/Advent01.scala) | | |
| 2022-02 | [Rock Paper Scissors](https://adventofcode.com/2022/day/2) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2022/Advent02.scala) | | |
| 2022-03 | [Rucksack Reorganization](https://adventofcode.com/2022/day/3) | [Scala](scala2/src/main/scala/jurisk/adventofcode/y2022/Advent03.scala) | | |
Expand Down
5 changes: 5 additions & 0 deletions scala2/src/main/resources/2023/17-test-2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
111111111111
999999999991
999999999991
999999999991
999999999991
13 changes: 13 additions & 0 deletions scala2/src/main/resources/2023/17-test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
2413432311323
3215453535623
3255245654254
3446585845452
4546657867536
1438598798454
4457876987766
3637877979653
4654967986887
4564679986453
1224686865563
2546548887735
4322674655533
141 changes: 141 additions & 0 deletions scala2/src/main/resources/2023/17.txt

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion scala2/src/main/scala/jurisk/adventofcode/Advent00.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,11 @@ object Advent00 {
def parseFile(fileName: String): Input =
parse(readFileText(fileName))

def fileName(suffix: String): String =
s"2023/02$suffix.txt"

def main(args: Array[String]): Unit = {
val realData: Input = parseFile("2023/02.txt")
val realData: Input = parseFile(fileName(""))

println(s"Part 1: ${part1(realData)}")
println(s"Part 2: ${part2(realData)}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,11 @@ object Advent16 {
solveBySimulation(field, Coords2D.Zero, Direction2D.W)

private[y2023] def part1Optimization(field: Input): Long =
solveByOptimization(field, (Coords2D.Zero, Direction2D.W).some, MinimizeOrMaximize.Minimize)
solveByOptimization(
field,
(Coords2D.Zero, Direction2D.W).some,
MinimizeOrMaximize.Minimize,
)

private[y2023] def part2Simulation(field: Input): Long = {
val solutions = edgeIncomings(field) map {
Expand Down
107 changes: 107 additions & 0 deletions scala2/src/main/scala/jurisk/adventofcode/y2023/Advent17.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package jurisk.adventofcode.y2023

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, Field2D}
import jurisk.utils.FileInput._
import jurisk.utils.Parsing.StringOps

object Advent17 {
type Input = Field2D[Int]

def parse(input: String): Input =
Field2D.parseDigitField(input)

final case class State(
coords: Coords2D,
direction: Option[CardinalDirection2D],
singleDirectionCounter: Int,
)

private def successors(
data: Input,
atMostInSingleDirection: Int,
minimumBeforeTurning: Int,
)(state: State): List[(State, Int)] = {
val candidateDirections = state.direction match {
case Some(lastDirection) =>
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
E :: S :: Nil
}

candidateDirections flatMap { direction =>
val nextCoords = state.coords + direction
data.at(nextCoords) map { heatLoss =>
val newState = State(
nextCoords,
direction.some,
if (direction.some == state.direction) {
state.singleDirectionCounter + 1
} else {
1
},
)
newState -> heatLoss
}
}
}

private def solve(
data: Input,
atMostInSingleDirection: Int,
minimumBeforeStoppingOrTurning: Int,
): Int = {
val calculateSuccessors = successors(
data,
atMostInSingleDirection,
minimumBeforeStoppingOrTurning,
) _

val result = Dijkstra.dijkstra[State, Int](
State(coords = data.topLeft, none, singleDirectionCounter = 0),
calculateSuccessors,
s =>
s.coords == data.bottomRight && s.singleDirectionCounter >= minimumBeforeStoppingOrTurning,
)

result match {
case Some((_, result)) => result
case None => "Failed to solve".fail
}
}

def part1(data: Input): Int =
solve(data, 3, 0)

def part2(data: Input): Int =
solve(data, 10, 4)

def parseFile(fileName: String): Input =
parse(readFileText(fileName))

def fileName(suffix: String): String =
s"2023/17$suffix.txt"

def main(args: Array[String]): Unit = {
val realData: Input = parseFile(fileName(""))

println(s"Part 1: ${part1(realData)}")
println(s"Part 2: ${part2(realData)}")
}
}
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
11 changes: 7 additions & 4 deletions scala2/src/test/scala/jurisk/adventofcode/Advent00Spec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,26 @@ import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers._

class Advent00Spec extends AnyFreeSpec {
private def testData = parseFile(fileName("-test"))
private def realData = parseFile(fileName(""))

"part 1" - {
"test" in {
part1(parseFile("2023/02-test.txt")) shouldEqual 0
part1(testData) shouldEqual 0
}

"real" in {
part1(parseFile("2023/02.txt")) shouldEqual 0
part1(realData) shouldEqual 0
}
}

"part 2" - {
"test" in {
part2(parseFile("2023/02-test.txt")) shouldEqual 0
part2(testData) shouldEqual 0
}

"real" in {
part2(parseFile("2023/02.txt")) shouldEqual 0
part2(realData) shouldEqual 0
}
}
}
16 changes: 10 additions & 6 deletions scala2/src/test/scala/jurisk/adventofcode/y2023/Advent16Spec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ class Advent16Spec extends AnyFreeSpec {
private val realData: Input = parseFile("2023/16.txt")

"solveByOptimization - simple maximization test that fails due to a loop" ignore {
val test = parse(
"""/-
|/|
|\/
|""".stripMargin)
val test = parse("""/-
|/|
|\/
|""".stripMargin)

solveByOptimization(test, (Coords2D.Zero, Direction2D.W).some, MinimizeOrMaximize.Maximize, debug = true) shouldEqual 1
solveByOptimization(
test,
(Coords2D.Zero, Direction2D.W).some,
MinimizeOrMaximize.Maximize,
debug = true,
) shouldEqual 1
}

"part 1" - {
Expand Down
35 changes: 35 additions & 0 deletions scala2/src/test/scala/jurisk/adventofcode/y2023/Advent17Spec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package jurisk.adventofcode.y2023

import org.scalatest.freespec.AnyFreeSpec
import Advent17._
import org.scalatest.matchers.should.Matchers._

class Advent17Spec extends AnyFreeSpec {
private def testData = parseFile(fileName("-test"))
private def testData2 = parseFile(fileName("-test-2"))
private def realData = parseFile(fileName(""))

"part 1" - {
"test" in {
part1(testData) shouldEqual 102
}

"real" in {
part1(realData) shouldEqual 686
}
}

"part 2" - {
"test" in {
part2(testData) shouldEqual 94
}

"test 2" in {
part2(testData2) shouldEqual 71
}

"real" in {
part2(realData) shouldEqual 801
}
}
}

0 comments on commit cda4dc7

Please sign in to comment.