Skip to content

Commit d9704ac

Browse files
committed
Day 20 part 1 complete.
1 parent db9249b commit d9704ac

File tree

5 files changed

+207
-3
lines changed

5 files changed

+207
-3
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2025 James Carman
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package adventofcode.day20
18+
19+
import adventofcode.util.geom.plane.Point2D
20+
import adventofcode.util.graph.Graphs
21+
import adventofcode.util.grid.TextGrid
22+
23+
fun String.countBestCheats(savingsThreshold: Int): Int {
24+
val map = TextGrid(lines())
25+
val start = map.coordinates().find { map[it] == 'S' }!!
26+
val end = map.coordinates().find { map[it] == 'E' }!!
27+
val vertices = map.coordinates().filter { map[it] != '#' }.toSet()
28+
val neighbors: (Point2D) -> List<Point2D> =
29+
{ p: Point2D -> p.neighbors().filter { it in map }.filter { map[it] != '#' } }
30+
val shortestPaths = Graphs.shortestPaths(
31+
start,
32+
vertices,
33+
neighbors
34+
)
35+
val shortestDistance = shortestPaths.distanceTo(end)
36+
val shortestPath = shortestPaths.pathTo(end)
37+
38+
val cheats = shortestPath.flatMap { p -> p.neighbors().filter { map[it] == '#' && !map.isEdge(it) } }.toSet()
39+
return cheats.count { cheat ->
40+
map[cheat] = '.'
41+
val newShortestPaths = Graphs.shortestPaths(start, vertices + cheat, neighbors)
42+
map[cheat] = '#'
43+
newShortestPaths.distanceTo(end) <= shortestDistance - savingsThreshold
44+
}
45+
}

src/main/kotlin/adventofcode/util/grid/Grid.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,6 @@ interface Grid<T : Any> {
9191
fun coordinatesWithValues() = coordinates().map { Pair(it, get(it)) }
9292

9393
fun pathsFrom(coordinate: Point2D) = Direction.entries.map { Path(this, it, coordinate) }
94+
95+
fun isEdge(coordinate: Point2D) = coordinate.x == 0 || coordinate.y == 0 || coordinate.x == width() - 1 || coordinate.y == height() - 1
9496
}

src/test/kotlin/adventofcode/Day20Test.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,20 @@
1616

1717
package adventofcode
1818

19+
import adventofcode.day20.countBestCheats
1920
import io.kotest.matchers.shouldBe
2021
import org.junit.jupiter.api.Test
2122

2223
class Day20Test {
2324

2425
@Test
2526
fun example1() {
26-
calculatePart1(readExample1()) shouldBe 0
27+
calculatePart1(readExample1(), 20) shouldBe 5
2728
}
2829

2930
@Test
3031
fun part1() {
31-
calculatePart1(readInput()) shouldBe 0
32+
calculatePart1(readInput(), 100) shouldBe 0
3233
}
3334

3435
@Test
@@ -41,7 +42,7 @@ class Day20Test {
4142
calculatePart2(readInput()) shouldBe 0
4243
}
4344

44-
private fun calculatePart1(input: String): Int = 0
45+
private fun calculatePart1(input: String, savingsThreshold:Int): Int = input.countBestCheats(savingsThreshold)
4546

4647
private fun calculatePart2(input: String): Int = 0
4748
}

src/test/resources/day20-example1.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
###############
2+
#...#...#.....#
3+
#.#.#.#.#.###.#
4+
#S#...#.#.#...#
5+
#######.#.#.###
6+
#######.#.#...#
7+
#######.#.###.#
8+
###..E#...#...#
9+
###.#######.###
10+
#...###...#...#
11+
#.#####.#.###.#
12+
#.#...#.#.#...#
13+
#.#.#.#.#.#.###
14+
#...#...#...###
15+
###############

0 commit comments

Comments
 (0)