-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay23.kt
122 lines (92 loc) · 3.82 KB
/
Day23.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package adventofcode2023
import java.io.File
import java.util.LinkedList
import kotlin.math.max
object Day23 {
private var inputs = File("resources/adventofcode2023/Day23.txt").readLines()
private const val FOREST = '#'
private const val EMPTY = '.'
private val slopeDirections = mapOf(
'^' to Vector2(0, -1),
'v' to Vector2(0, 1),
'>' to Vector2(1, 0),
'<' to Vector2(-1, 0)
)
private val startPoint = Vector2(1, 0)
private val endPoint = Vector2(inputs[0].length - 2, inputs.size - 1)
private data class Vector2(val x: Int, val y: Int) {
fun add(other: Vector2) = Vector2(x + other.x, y + other.y)
fun inBounds(): Boolean = (x >= 0 && y >= 0 && x < inputs.size && y < inputs[0].length)
fun char(): Char = inputs[y][x]
fun neighbors(slipperySlopes: Boolean = true): Set<Vector2> {
val neighbors = mutableSetOf<Vector2>()
if (!slipperySlopes || char() == EMPTY) {
slopeDirections.values.forEach {
val next = add(it)
if (next.inBounds() && next.char() != FOREST) {
neighbors.add(next)
}
}
} else {
val next = add(slopeDirections[char()]!!)
if (next.inBounds() && next.char() != FOREST) neighbors.add(next)
}
return neighbors
}
}
private fun searchCrossings(crossingLocation: Vector2, slipperySlopes: Boolean = true): List<Pair<Vector2, Int>> {
val crossings = mutableListOf<Pair<Vector2, Int>>()
crossingLocation.neighbors().forEach { start ->
var current = start
var currentNeighbors = current.neighbors(slipperySlopes).filter { it != crossingLocation }
var distance = 1
while (currentNeighbors.size == 1) {
val next = currentNeighbors.single()
if (current == endPoint || current == startPoint) break
currentNeighbors = next.neighbors(slipperySlopes).filter { it != current }
current = next
distance++
}
if (current == endPoint || current == startPoint || currentNeighbors.isNotEmpty())
crossings.add(Pair(current, distance))
}
return crossings
}
private fun crossingGraph(slipperySlopes: Boolean = true): Map<Vector2, List<Pair<Vector2, Int>>> {
val graph = mutableMapOf<Vector2, List<Pair<Vector2, Int>>>()
graph[startPoint] = searchCrossings(startPoint, slipperySlopes)
for (y in inputs.indices) {
for (x in inputs[y].indices) {
if (inputs[y][x] != FOREST) {
val location = Vector2(x, y)
if (location.neighbors().size > 2) {
graph[location] = searchCrossings(location, slipperySlopes)
}
}
}
}
return graph
}
private fun longestHike(graph: Map<Vector2, List<Pair<Vector2, Int>>>): Int {
val histories = LinkedList(listOf(Pair(0, listOf(Vector2(1, 0)))))
var longest = 0
while (histories.isNotEmpty()) {
val current = histories.removeFirst()
val lastStep = current.second.last()
if (lastStep == endPoint) {
longest = max(longest, current.first)
continue
}
graph[lastStep]!!.filterNot { current.second.contains(it.first) }.forEach {
histories.add(Pair(current.first + it.second, current.second + listOf(it.first)))
}
}
return longest
}
fun part1() = println(longestHike(crossingGraph()))
fun part2() = println(longestHike(crossingGraph(false)))
}
fun main() {
Day23.part1()
Day23.part2()
}