-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay21.kt
145 lines (132 loc) · 5.49 KB
/
Day21.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package aoc2023.day21
import util.Coord
import kotlin.math.abs
fun part1(map: Map<Coord, Char>, steps: Int = 64): Int {
val start = map.entries.first { it.value == 'S' }.key
var edge = start.neighbors()
.filter { map[it] == '.' }
.toSet()
repeat(steps - 1) {
val newEdge = mutableSetOf<Coord>()
for (plot in edge) {
for (neighbor in plot.neighbors()) {
if (map[neighbor] == '.' || map[neighbor] == 'S') {
newEdge.add(neighbor)
}
}
}
edge = newEdge
}
return edge.size
}
fun part2(map: Map<Coord, Char>, steps: Int = 26501365): Long {
val (height, width) = Pair(map.keys.maxBy { it.y }.y + 1, map.keys.maxBy { it.x }.x + 1)
val stepsInt = steps / width
val stepsRem = steps.mod(width)
// Direction vectors for the four cardinal directions
val (dirX, dirY) = Pair(
intArrayOf(1, 0, -1, 0),
intArrayOf(0, 1, 0, -1)
)
for (y0 in 0 until height) {
for (x0 in 0 until width) {
if (map[Coord(x0, y0)] == 'S') {
var queue0 = ArrayDeque<Coord>()
var queue1 = ArrayDeque<Coord>()
val visited = HashSet<Coord>()
// Enqueue a new coordinate if it's valid
fun enqueue(x: Int, y: Int) {
if (map[Coord(x.mod(width), y.mod(height))] == '#')
return
val coord = Coord(x, y)
if (coord in visited)
return
visited += coord
queue1.add(Coord(x, y))
}
enqueue(x0, y0)
// Calculate the sum within a diamond shape
fun sumDiamond(xx: Int, yy: Int, shape: Int): Long {
var sum = 0L
val x1 = x0 + xx * width + shape * stepsRem
val y1 = y0 + yy * height + shape * stepsRem
when (shape) {
0 -> for (x in x1 - stepsRem..x1 + stepsRem + stepsRem) {
val diff = stepsRem - abs(x - x1)
for (y in y1 - diff..y1 + diff) {
if (Coord(x, y) in visited)
sum++
}
}
1 -> for (x in x1 - stepsRem + 1..x1 + stepsRem) {
val diff = if (x <= x1) stepsRem - (x1 - x) else stepsRem - (x - x1 - 1)
for (y in y1 - diff + 1..y1 + diff) {
if (Coord(x, y) in visited)
sum++
}
}
}
return sum
}
val ranges = Array(2) { LongArray(2) }
// Calculate the sum within a specified range
fun computed(diff: Int): Long {
var sum = 0L
for (i in -diff..diff) {
val w = diff - abs(i)
val t0 = (abs(i) + w + diff) % 2
sum += ranges[t0][0] * (w + 1) + ranges[1 - t0][0] * w
}
for (i in -diff until diff) {
val w = if (i < 0) diff + i + 1 else diff - i
sum += ranges[0][1] * w + ranges[1][1] * w
}
return sum
}
var current = 0
while (true) {
if (queue0.isEmpty()) {
queue0 = queue1.also { queue1 = queue0 }
if (current % width == stepsRem) {
val rep = current / width
for (sh in 0..1) {
for (xx in -1..0) {
val mod = (xx + rep).mod(2)
if (ranges[mod][sh] == 0L) ranges[mod][sh] = sumDiamond(xx, 0, sh) else check(
ranges[mod][sh] == sumDiamond(
xx, 0, sh
)
)
}
}
val all = queue0.size.toLong()
val computed = computed(rep)
check(all == computed)
var td = 0L
for (yy in -rep..rep) {
for (xx in -rep..rep) {
val sd = sumDiamond(xx, yy, 0)
td += sd
}
for (xx in -rep..rep) {
val sd = sumDiamond(xx, yy, 1)
td += sd
}
}
check(all == td)
if (rep == 2) {
return computed(stepsInt)
}
}
visited.clear()
current++
}
val (x, y) = queue0.removeFirst()
for (d in 0..3)
enqueue(x + dirX[d], y + dirY[d])
}
}
}
}
return 0L
}