Skip to content

Commit 69a2a27

Browse files
authored
Add solution and input for 2023 day 17 (#502)
1 parent 8cab17b commit 69a2a27

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

2023/input/day17

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2413432311323
2+
3215453535623
3+
3255245654254
4+
3446585845452
5+
4546657867536
6+
1438598798454
7+
4457876987766
8+
3637877979653
9+
4654967986887
10+
4564679986453
11+
1224686865563
12+
2546548887735
13+
4322674655533

2023/src/day17.scala

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package day17
2+
// based on solution from https://github.com/stewSquared/adventofcode/blob/src/main/scala/2023/Day17.worksheet.sc
3+
4+
import locations.Directory.currentDir
5+
import inputs.Input.loadFileSync
6+
7+
@main def part1: Unit =
8+
println(s"The solution is ${search(_.nextStates)}")
9+
10+
@main def part2: Unit =
11+
println(s"The solution is ${search(_.nextStates2)}")
12+
13+
def loadInput(): Vector[Vector[Int]] = Vector.from:
14+
val file = loadFileSync(s"$currentDir/../input/day17")
15+
for line <- file.split("\n")
16+
yield line.map(_.asDigit).toVector
17+
18+
enum Dir:
19+
case N, S, E, W
20+
21+
def turnRight = this match
22+
case Dir.N => E
23+
case Dir.E => S
24+
case Dir.S => W
25+
case Dir.W => N
26+
27+
def turnLeft = this match
28+
case Dir.N => W
29+
case Dir.W => S
30+
case Dir.S => E
31+
case Dir.E => N
32+
33+
val grid = loadInput()
34+
35+
val xRange = grid.head.indices
36+
val yRange = grid.indices
37+
38+
case class Point(x: Int, y: Int):
39+
def inBounds = xRange.contains(x) && yRange.contains(y)
40+
41+
def move(dir: Dir) = dir match
42+
case Dir.N => copy(y = y - 1)
43+
case Dir.S => copy(y = y + 1)
44+
case Dir.E => copy(x = x + 1)
45+
case Dir.W => copy(x = x - 1)
46+
47+
def heatLoss(p: Point) =
48+
if p.inBounds then grid(p.y)(p.x) else 0
49+
50+
case class State(pos: Point, dir: Dir, streak: Int, totalHeatLoss: Int):
51+
def straight: State =
52+
val newPos = pos.move(dir)
53+
State(pos.move(dir), dir, streak + 1, totalHeatLoss + heatLoss(newPos))
54+
55+
def turnLeft: State =
56+
val newDir = dir.turnLeft
57+
val newPos = pos.move(newDir)
58+
State(newPos, newDir, 1, totalHeatLoss + heatLoss(newPos))
59+
60+
def turnRight: State =
61+
val newDir = dir.turnRight
62+
val newPos = pos.move(newDir)
63+
State(pos.move(newDir), newDir, 1, totalHeatLoss + heatLoss(newPos))
64+
65+
def nextStates: List[State] =
66+
List(straight, turnLeft, turnRight).filter: s =>
67+
s.pos.inBounds && s.streak <= 3
68+
69+
def nextStates2: List[State] =
70+
if streak < 4 then List(straight)
71+
else List(straight, turnLeft, turnRight).filter: s =>
72+
s.pos.inBounds && s.streak <= 10
73+
74+
def motion = (pos, dir, streak)
75+
76+
def search(next: State => List[State]): Int =
77+
import collection.mutable.{PriorityQueue, Set}
78+
79+
given Ordering[State] = Ordering.by(_.totalHeatLoss)
80+
val pq = PriorityQueue.empty[State].reverse
81+
82+
var visiting = State(Point(0, 0), Dir.E, 0, 0)
83+
val visited = Set(visiting.motion)
84+
85+
val end = Point(xRange.max, yRange.max)
86+
while visiting.pos != end do
87+
val states = next(visiting).filterNot(s => visited(s.motion))
88+
states.foreach(s => s"enqueuing: $s")
89+
pq.enqueue(states*)
90+
visited ++= states.map(_.motion)
91+
visiting = pq.dequeue()
92+
93+
visiting.totalHeatLoss

0 commit comments

Comments
 (0)