Skip to content

Commit 97c8086

Browse files
committed
Refactoring Day 14 to use entropy to find the least random configuration of points to detect the tree
1 parent 88ec3d8 commit 97c8086

File tree

5 files changed

+73
-47
lines changed

5 files changed

+73
-47
lines changed

src/main/kotlin/adventofcode/day14/RestroomRedoubt.kt

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import adventofcode.util.lcmInt
2323
import adventofcode.util.occurrences
2424
import adventofcode.util.removeAll
2525
import adventofcode.util.splitsAsInt
26+
import kotlin.math.ln
27+
import kotlin.math.roundToInt
2628

2729

2830
data class Robot(val ray: Ray) {
@@ -50,13 +52,13 @@ fun List<Point2D>.safetyFactor(maxX: Int, maxY: Int): Int {
5052
val midY = maxY / 2
5153
return filter { it.x != midX && it.y != midY }
5254
.map { (x, y) ->
53-
when {
54-
x < midX && y < midY -> "1"
55-
x > midX && y < midY -> "2"
56-
x < midX && y > midY -> "3"
57-
else -> "4"
55+
when {
56+
x < midX && y < midY -> "1"
57+
x > midX && y < midY -> "2"
58+
x < midX && y > midY -> "3"
59+
else -> "4"
60+
}
5861
}
59-
}
6062
.occurrences()
6163
.values.reduce(Int::times)
6264
}
@@ -76,36 +78,25 @@ fun Set<Point2D>.printGrid(maxX: Int, maxY: Int) {
7678
println(grid)
7779
}
7880

79-
fun String.findTree(): Long {
81+
fun List<Point2D>.calculateShannonEntropy(maxX: Int, maxY: Int, gridWidth: Int, gridHeight: Int): Double {
82+
val cellWidth = maxX * 1.0 / gridWidth
83+
val cellHeight = maxY * 1.0 / gridHeight
84+
return groupBy { (x, y) -> Point2D((x / cellWidth).roundToInt(), (y / cellHeight).roundToInt()) }
85+
.values
86+
.map { it.size.toDouble() / size }
87+
.sumOf { -it * ln(it) }
88+
}
89+
90+
91+
fun String.findTree(): Int {
8092
val robots = parseRobots()
8193
val maxX = robots.maxOf { it.ray.origin.x } + 1
8294
val maxY = robots.maxOf { it.ray.origin.y } + 1
95+
val sequences = robots.map { it.points(maxX, maxY) }
8396

84-
val sequences = robots.map { it.points(maxX, maxY) }.toList()
85-
86-
var t = 1
87-
val maxT = lcmInt(maxX, maxY)
88-
var maxCount = 0
89-
while (t <= maxT) {
90-
val points = sequences.map { it.next() }.toSet()
91-
92-
val count = points.count { p -> setOf(p.northEast(), p.northWest(), p.southEast(), p.southWest()).any { it in points } }
93-
if(count > maxCount) {
94-
println("t = ${t}")
95-
points.printGrid(maxX, maxY)
96-
println()
97-
maxCount = count
98-
}
99-
//if(points.groupBy { it.y }.values.any{it.size > 3}) {
100-
101-
//}
102-
// while (points.size >= tree.size) {
103-
// if (points.containsAll(tree)) {
104-
// return t
105-
// }
106-
// points = points.filter { it.y > 0 }.map { it.north() }.toSet()
107-
// }
108-
t++
97+
val minEntropyT = (1..lcmInt(maxX, maxY)).minBy { t ->
98+
sequences.map { it.next() }.calculateShannonEntropy(maxX, maxY, 10, 10)
10999
}
110-
return -1L
100+
robots.map { it.move(minEntropyT, maxX, maxY) }.toSet().printGrid(maxX, maxY)
101+
return minEntropyT
111102
}

src/main/kotlin/adventofcode/util/graph/Graph.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ interface Graph<V : Any, E : Any> {
3434
fun dfs(start: V, end: V): List<V> = Graphs.dfs(start, end, ::neighbors)
3535
fun bfs(start: V, end: V): List<V> = Graphs.bfs(start, end, ::neighbors)
3636

37+
fun dfsTraversal(start: V): List<V> = Graphs.dfsTraversal(start, ::neighbors)
38+
fun bfsTraversal(start: V): List<V> = Graphs.bfsTraversal(start, ::neighbors)
39+
3740
fun reachable(start: V, maxSteps: Int) = Graphs.reachable(start, maxSteps, ::neighbors)
3841

3942
}

src/main/kotlin/adventofcode/util/graph/Graphs.kt

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@ object Graphs {
4040
val visited = mutableSetOf<V>()
4141
vertices.forEach { vertex -> dist[vertex] = Double.POSITIVE_INFINITY }
4242
dist[start] = 0.0
43-
val queue = PriorityQueue { l: V, r: V -> compareDoubles(dist.getOrDefault(l, Double.MAX_VALUE), dist.getOrDefault(r, Double.MAX_VALUE)) }
43+
val queue = PriorityQueue { l: V, r: V ->
44+
compareDoubles(
45+
dist.getOrDefault(l, Double.MAX_VALUE),
46+
dist.getOrDefault(r, Double.MAX_VALUE)
47+
)
48+
}
4449
queue.add(start)
4550
while (queue.isNotEmpty()) {
4651
val vertex = queue.poll()
@@ -77,11 +82,19 @@ object Graphs {
7782
}
7883

7984
fun <V> dfs(start: V, end: V, neighbors: (V) -> List<V>): List<V> {
80-
return search(start, end, neighbors) { list, element -> list.add(0, element) }
85+
return search(start, end, neighbors) { addFirst(it) }
8186
}
8287

8388
fun <V> bfs(start: V, end: V, neighbors: (V) -> List<V>): List<V> {
84-
return search(start, end, neighbors) { list, element -> list.add(element) }
89+
return search(start, end, neighbors) { addLast(it) }
90+
}
91+
92+
fun <V> dfsTraversal(start: V, neighbors: (V) -> List<V>): List<V> {
93+
return traverse(start, neighbors) { addFirst(it) }
94+
}
95+
96+
fun <V> bfsTraversal(start: V, neighbors: (V) -> List<V>): List<V> {
97+
return traverse(start, neighbors) { addLast(it) }
8598
}
8699

87100
fun <V> allPaths(start: V, end: V, neighbors: (V) -> List<V>): List<List<V>> {
@@ -101,7 +114,7 @@ object Graphs {
101114
return paths
102115
}
103116

104-
fun <V> connectedComponents(vertices:Set<V>, neighbors: (V) -> List<V>): List<Set<V>> {
117+
fun <V> connectedComponents(vertices: Set<V>, neighbors: (V) -> List<V>): List<Set<V>> {
105118
val components = mutableListOf<Set<V>>()
106119
val visited = mutableSetOf<V>()
107120
vertices.forEach { vertex ->
@@ -114,17 +127,34 @@ object Graphs {
114127
return components
115128
}
116129

130+
private fun <V> traverse(
131+
start: V,
132+
neighbors: (V) -> List<V>,
133+
append: MutableList<V>.(V) -> Unit
134+
): List<V> {
135+
val visited = mutableSetOf(start)
136+
val vertices = mutableListOf(start)
137+
val traversal = mutableListOf<V>()
138+
while (vertices.isNotEmpty()) {
139+
val vertex = vertices.removeFirst()
140+
traversal.add(vertex)
141+
neighbors(vertex).filter { it !in visited }.forEach { neighbor ->
142+
visited += neighbor
143+
vertices.append(neighbor)
144+
}
145+
}
146+
return traversal
147+
}
148+
117149
private fun <V> search(
118150
start: V,
119151
end: V,
120152
neighbors: (V) -> List<V>,
121-
add: (MutableList<List<V>>, List<V>) -> Unit
153+
append: MutableList<List<V>>.(List<V>) -> Unit
122154
): List<V> {
123155
val visited = mutableSetOf<V>()
124156
visited += start
125-
val paths = mutableListOf<List<V>>()
126-
add(paths, listOf(start))
127-
157+
val paths = mutableListOf(listOf(start))
128158
while (paths.isNotEmpty()) {
129159
val path = paths.removeFirst()
130160
val terminus = path.last()
@@ -133,7 +163,7 @@ object Graphs {
133163
}
134164
neighbors(terminus).filter { it !in visited }.forEach { neighbor ->
135165
visited += neighbor
136-
add(paths, path + neighbor)
166+
paths.append(path + neighbor)
137167
}
138168
}
139169
return listOf()

src/test/kotlin/adventofcode/Day14Test.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package adventofcode
1818

1919
import adventofcode.day14.calculateSafetyFactorAt
2020
import adventofcode.day14.findTree
21+
import io.kotest.matchers.ints.shouldBeGreaterThan
2122

2223
import io.kotest.matchers.shouldBe
2324
import org.junit.jupiter.api.Test
@@ -36,15 +37,15 @@ class Day14Test {
3637

3738
@Test
3839
fun example2() {
39-
calculatePart2(readExample2()) shouldBe -1
40+
calculatePart2(readExample2()) shouldBeGreaterThan 0
4041
}
4142

4243
@Test
4344
fun part2() {
44-
calculatePart2(readInput()) shouldBe -1
45+
calculatePart2(readInput()) shouldBe 7709
4546
}
4647

4748
private fun calculatePart1(input: String): Int = input.calculateSafetyFactorAt(100)
4849

49-
private fun calculatePart2(input: String): Long = input.findTree()
50+
private fun calculatePart2(input: String): Int = input.findTree()
5051
}

src/test/kotlin/adventofcode/util/graph/GraphsTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package adventofcode.util.graph
1818

1919
import adventofcode.util.geom.plane.Point2D
20+
import io.kotest.matchers.shouldBe
2021
import org.assertj.core.api.Assertions.assertThat
2122
import org.junit.jupiter.api.Test
2223

@@ -40,7 +41,7 @@ class GraphsTest {
4041
Point2D(-1, 0),
4142
Point2D(0, -1)
4243
)
43-
44-
assertThat(Graphs.reachable(Point2D.origin()) {it.neighbors().filter { it in searchSpace }}).hasSize(5)
44+
assertThat(Graphs.reachable(Point2D.origin()) { it.neighbors().filter { n -> n in searchSpace } }).hasSize(5)
4545
}
46+
4647
}

0 commit comments

Comments
 (0)