Skip to content

Commit 44b694b

Browse files
committed
Add 2022 day 12 part 1
1 parent 843ae05 commit 44b694b

File tree

4 files changed

+221
-0
lines changed

4 files changed

+221
-0
lines changed

2022/src/main/kotlin/day12/Day12.kt

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package day12
2+
3+
import shared.Point
4+
5+
6+
class Day12 {
7+
8+
companion object {
9+
10+
fun part1(input: List<String>): Int {
11+
val graph = parseInput(input)
12+
13+
val start = graph.find {
14+
it.char == 'S'
15+
}!!
16+
val target = graph.find {
17+
it.char == 'E'
18+
}!!
19+
20+
val distances = findShortestPath(graph, start)
21+
return distances[target] ?: -1
22+
}
23+
24+
fun part2(input: List<String>): Int {
25+
// val graph = parseInput(input)
26+
//
27+
// val start = graph.find {
28+
// it.char == 'E'
29+
// }!!
30+
//
31+
// val distances = findShortestPath(graph, start)
32+
//
33+
// return distances.filter {
34+
// it.key.char == 'a'
35+
// }.values.minOrNull() ?: -1
36+
37+
38+
// val graph = parseInput(input)
39+
//
40+
// val starts = graph.filter {
41+
// it.char == 'a'
42+
// }.toList()
43+
//
44+
//
45+
// val target = graph.find {
46+
// it.char == 'E'
47+
// }!!
48+
// var min = Int.MAX_VALUE
49+
//
50+
// for (start in starts) {
51+
// val distances = findShortestPath(graph, start)
52+
// val newMin = distances[target] ?: Int.MAX_VALUE
53+
//
54+
// if (newMin < min) {
55+
// min = newMin
56+
// }
57+
// }
58+
// return min
59+
60+
return -1
61+
}
62+
63+
private fun findShortestPath(locations: List<Location>, start: Location): Map<Location, Int> {
64+
val distances = mutableMapOf<Location, Int>()
65+
val pathStartToAny = mutableMapOf<Location, Location?>()
66+
67+
// Initialize
68+
locations.forEach {
69+
distances[it] = Int.MAX_VALUE
70+
pathStartToAny[it] = null
71+
}
72+
73+
// Set distance from start node to 0
74+
distances[start] = 0
75+
76+
val queue = locations.toMutableList()
77+
78+
while (queue.isNotEmpty()) {
79+
val u = extractMin(queue, distances)
80+
queue.remove(u)
81+
82+
for ((neighbor, distance) in u.neighbors) {
83+
// Relaxation
84+
if (distances[neighbor]!! > distances[u]!! + distance) {
85+
distances[neighbor] = distances[u]!! + distance
86+
pathStartToAny[neighbor] = u
87+
}
88+
}
89+
}
90+
91+
return distances
92+
}
93+
94+
private fun extractMin(queue: List<Location>, distances: Map<Location, Int>): Location {
95+
var min = queue[0]
96+
97+
for (location in queue) {
98+
if (distances[location]!! < distances[min]!!) {
99+
min = location
100+
}
101+
}
102+
return min
103+
}
104+
105+
data class Location(val char: Char, val point: Point) {
106+
val neighbors = mutableMapOf<Location, Int>() // Target, distance
107+
108+
fun addNeighbor(possibleNeighbor: Location) {
109+
// Only allow movement to smaller or at most 1 higher
110+
if (possibleNeighbor.elevation() <= elevation() + 1) {
111+
neighbors[possibleNeighbor] = 1
112+
}
113+
}
114+
115+
private fun elevation(): Char = when (char) {
116+
'S' -> 'a'
117+
'E' -> 'z'
118+
else -> char
119+
}
120+
}
121+
122+
private fun parseInput(input: List<String>): List<Location> {
123+
val locationsMap = mutableListOf<List<Location>>()
124+
125+
// Create nodes
126+
input.forEachIndexed { y, row ->
127+
val locationsRow = mutableListOf<Location>()
128+
row.forEachIndexed { x, char ->
129+
locationsRow.add(Location(char, Point(x, y)))
130+
}
131+
locationsMap.add(locationsRow)
132+
}
133+
134+
135+
// Set neighbors
136+
val lastYIndex = locationsMap.lastIndex
137+
val lastXIndex = locationsMap[0].lastIndex
138+
139+
locationsMap.forEachIndexed { y, row ->
140+
row.forEachIndexed { x, location ->
141+
if (y > 0) {
142+
location.addNeighbor(locationsMap[y - 1][x])
143+
}
144+
if (y < lastYIndex) {
145+
location.addNeighbor(locationsMap[y + 1][x])
146+
}
147+
if (x > 0) {
148+
location.addNeighbor(locationsMap[y][x - 1])
149+
}
150+
if (x < lastXIndex) {
151+
location.addNeighbor(locationsMap[y][x + 1])
152+
}
153+
}
154+
}
155+
156+
return locationsMap.flatten()
157+
}
158+
}
159+
}

2022/src/main/kotlin/shared/Grid.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package shared
2+
3+
import day09.Day09
4+
5+
enum class Direction(val x: Int, val y: Int) {
6+
UP(0, 1),
7+
RIGHT(1, 0),
8+
DOWN(0, -1),
9+
LEFT(-1, 0),
10+
UP_RIGHT(1, 1),
11+
UP_LEFT(-1, 1),
12+
DOWN_RIGHT(1, -1),
13+
DOWN_LEFT(-1, -1);
14+
15+
companion object {
16+
val xyDirections = listOf(UP, RIGHT, DOWN, LEFT)
17+
}
18+
}
19+
20+
data class Point(var x: Int = 0, var y: Int = 0) {
21+
fun newPointWithDirection(direction: Direction) = Point(x + direction.x, y + direction.y)
22+
23+
fun applyDirection(direction: Day09.Companion.Direction) {
24+
x += direction.x
25+
y += direction.y
26+
}
27+
}

2022/src/test/kotlin/Day12Test.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import day12.Day12
2+
import org.junit.jupiter.api.Test
3+
4+
import org.junit.jupiter.api.Assertions.*
5+
6+
internal class Day12Test {
7+
8+
private val input = Resource.readFileLines("day12")
9+
private val testInput = Resource.readFileLines("day12_test")
10+
11+
@Test
12+
fun `part1 example`() {
13+
assertEquals(31, Day12.part1(testInput))
14+
}
15+
16+
@Test
17+
fun `part1 puzzel input`() {
18+
assertEquals(468, Day12.part1(input))
19+
}
20+
21+
@Test
22+
fun `part2 example`() {
23+
assertEquals(29, Day12.part2(testInput))
24+
}
25+
26+
@Test
27+
fun `part2 puzzel input`() {
28+
assertEquals(0, Day12.part2(input))
29+
}
30+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Sabqponm
2+
abcryxxl
3+
accszExk
4+
acctuvwj
5+
abdefghi

0 commit comments

Comments
 (0)