generated from kotlin-hands-on/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay14.kt
96 lines (83 loc) · 2.81 KB
/
Day14.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
data class Position(val x: Int, val y: Int)
fun Position.below() = Position(x, y + 1)
fun Position.left() = Position(x - 1, y + 1)
fun Position.right() = Position(x + 1, y + 1)
fun createOccupiedSet(paths: List<List<Position>>): MutableSet<Position> {
return buildSet {
paths.forEach { path ->
path.zipWithNext().forEach { (p1, p2) ->
val (x1, y1) = p1
val (x2, y2) = p2
(minOf(y1, y2)..maxOf(y1, y2)).forEach { y ->
(minOf(x1, x2)..maxOf(x1, x2)).forEach { x ->
add(Position(x, y))
}
}
}
}
}.toMutableSet()
}
fun Position.computeNext(occupied: MutableSet<Position>) = sequence {
yield(below())
yield(left())
yield(right())
}.firstOrNull { it !in occupied }
inline fun runSimulation(
testInput: List<String>,
drop: (MutableSet<Position>, Int, Position, Position?) -> Position?
): Int {
val paths: List<List<Position>> = testInput.map { line ->
line.split(" -> ").map { coordinates ->
val (x, y) = coordinates.split(',').map(String::toInt)
Position(x, y)
}
}
val yMax = paths.maxOf { path -> path.maxOf(Position::y) }
val occupied = createOccupiedSet(paths)
val initialSize = occupied.size
runCatching {
while (true) {
var s = Position(500, 0)
while (true) {
val proposedNext = s.computeNext(occupied)
val next = drop(occupied, yMax, s, proposedNext)
if (next == null) {
occupied += s
break
}
s = next
}
}
}
return occupied.size - initialSize
}
fun main() {
fun part1(testInput: List<String>): Int {
return runSimulation(testInput) { _, yMax, s, next ->
// Run until sand doesn't fall past yMax
check(s.y <= yMax)
next
}
}
fun part2(testInput: List<String>): Int {
return runSimulation(testInput) { occupied, yMax, s, next ->
// Hit the floor - override next value
if (s.y == yMax + 1) {
occupied += s
return@runSimulation null
}
// Run until we're NOT stuck in the starting position
check((next == null && s == Position(500, 0)).not()) {
// Mark starting position occupied before wrapping up
occupied.add(s)
}
next
}
}
val testInput = readInput("Day14_test")
check(part1(testInput).also(::println) == 24)
check(part2(testInput).also(::println) == 93)
val input = readInput("Day14")
check(part1(input).also(::println) == 692)
check(part2(input).also(::println) == 31706)
}