-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay14.kt
110 lines (93 loc) · 3.36 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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
private enum class Cell {
RoundedRock,
CubeRock,
Empty,
}
private fun rollNorthIndex(fallenMap: List<List<Cell>>, initialIndex: Index2): Index2 {
var row = initialIndex.i
while (row != 0 && fallenMap[row - 1][initialIndex.j] == Cell.Empty) row--
return initialIndex.copy(i = row)
}
private fun rollSouthIndex(fallenMap: List<List<Cell>>, initialIndex: Index2): Index2 {
var row = initialIndex.i
while (row != fallenMap.lastIndex && fallenMap[row + 1][initialIndex.j] == Cell.Empty) row++
return initialIndex.copy(i = row)
}
private fun rollWestIndex(fallenMap: List<List<Cell>>, initialIndex: Index2): Index2 {
var column = initialIndex.j
while (column != 0 && fallenMap[initialIndex.i][column - 1] == Cell.Empty) column--
return initialIndex.copy(j = column)
}
private fun rollEastIndex(fallenMap: List<List<Cell>>, initialIndex: Index2): Index2 {
var column = initialIndex.j
while (column != fallenMap.first().lastIndex && fallenMap[initialIndex.i][column + 1] == Cell.Empty) column++
return initialIndex.copy(j = column)
}
private fun Sequence<Indexed2Value<Cell>>.roll(
fallenMap: List<MutableList<Cell>>,
getIndex: (List<List<Cell>>, Index2) -> Index2,
) {
filter { it.value == Cell.RoundedRock }.map { it.index2 }.forEach { index ->
fallenMap[index] = Cell.Empty
fallenMap[getIndex(fallenMap, index)] = Cell.RoundedRock
}
}
private fun rollRocks(
fallenMap: List<MutableList<Cell>>,
getIndex: (List<List<Cell>>, Index2) -> Index2,
) {
fallenMap.indexed2Sequence().roll(fallenMap, getIndex)
}
private fun rollRocksBackwards(
fallenMap: List<MutableList<Cell>>,
getIndex: (List<List<Cell>>, Index2) -> Index2,
) {
fallenMap.asReversed().asSequence()
.flatMapIndexed { i, row ->
row.asReversed().mapIndexed { j, value ->
Indexed2Value(Index2(fallenMap.lastIndex - i, fallenMap.first().lastIndex - j), value)
}
}
.roll(fallenMap, getIndex)
}
private fun calculateLoad(map: List<List<Cell>>): Int =
map.asReversed().sumOfIndexed { i, row ->
(i + 1) * row.asSequence().filter { it == Cell.RoundedRock }.count()
}
private fun part1(map: List<List<Cell>>): Int {
val fallenMap = map.mutableDeepCopy()
rollRocks(fallenMap, ::rollNorthIndex)
return calculateLoad(fallenMap)
}
private fun part2(map: List<List<Cell>>): Int {
val fallenMap = map.mutableDeepCopy()
val visited = mutableMapOf<List<List<Cell>>, Int>()
var i = 0
while (true) {
if (fallenMap in visited) break
visited[fallenMap.deepCopy()] = i++
rollRocks(fallenMap, ::rollNorthIndex)
rollRocks(fallenMap, ::rollWestIndex)
rollRocksBackwards(fallenMap, ::rollSouthIndex)
rollRocksBackwards(fallenMap, ::rollEastIndex)
}
val cycleStart = visited[fallenMap]!!
val index = cycleStart + 1_000_000_000 % (i - cycleStart - 1) - 1
val targetMap = visited.asSequence().find { it.value == index }!!.key
return calculateLoad(targetMap)
}
fun main() {
val map = mapLines { line ->
line.map {
when (it) {
'O' -> Cell.RoundedRock
'#' -> Cell.CubeRock
'.' -> Cell.Empty
else -> expect()
}
}
}
val first = part1(map)
val second = part2(map)
println("$first $second")
}