Skip to content

Commit 67eed65

Browse files
committed
Solution Day 20, part one (Race Condition)
1 parent f58d055 commit 67eed65

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

src/main/kotlin/Day20.kt

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import de.ronny_h.extensions.*
2+
import de.ronny_h.extensions.Direction.*
3+
4+
fun main() {
5+
val day = "Day20"
6+
7+
fun part1(input: List<String>, minPicosecondsSaved: Int): Int {
8+
val track = RaceTrack(input)
9+
track.printGrid()
10+
val baseline = track.shortestPath().distance
11+
return track.countAllShortcutsShorterThan(baseline - minPicosecondsSaved)
12+
}
13+
14+
fun part1Small(input: List<String>) = part1(input, 10)
15+
fun part1Large(input: List<String>) = part1(input, 100)
16+
17+
fun part2(input: List<String>): Int {
18+
return input.size
19+
}
20+
21+
println("$day part 1")
22+
23+
val testInput = """
24+
###############
25+
#...#...#.....#
26+
#.#.#.#.#.###.#
27+
#S#...#.#.#...#
28+
#######.#.#.###
29+
#######.#.#...#
30+
#######.#.###.#
31+
###..E#...#...#
32+
###.#######.###
33+
#...###...#...#
34+
#.#####.#.###.#
35+
#.#...#.#.#...#
36+
#.#.#.#.#.#.###
37+
#...#...#...###
38+
###############
39+
""".trimIndent().split('\n')
40+
printAndCheck(testInput, ::part1Small, 10)
41+
42+
val input = readInput(day)
43+
printAndCheck(input, ::part1Large, 1438)
44+
45+
46+
println("$day part 2")
47+
48+
printAndCheck(testInput, ::part2, 31)
49+
printAndCheck(input, ::part2, 18805872)
50+
}
51+
52+
private class RaceTrack(input: List<String>) : Grid<Char>(input, '#') {
53+
private val wall = nullElement
54+
private val free = '.'
55+
override fun Char.toElementType(): Char = this
56+
57+
private val start = find('S')
58+
private val goal = find('E')
59+
60+
private fun find(symbol: Char): Coordinates = forEachElement { row, col, elem ->
61+
when (elem) {
62+
symbol -> Coordinates(row, col)
63+
else -> null
64+
}
65+
}.filterNotNull().first()
66+
67+
fun shortestPath(): ShortestPath<Coordinates> {
68+
val neighbours: (Coordinates) -> List<Coordinates> = { node ->
69+
Direction
70+
.entries
71+
.map { node + it }
72+
.filter { getAt(node) != wall }
73+
}
74+
75+
val d: (Coordinates, Coordinates) -> Int = { a, b ->
76+
// pre-condition: a and b are neighbours
77+
1
78+
}
79+
80+
val h: (Coordinates) -> Int = { it taxiDistanceTo goal }
81+
82+
return aStar(start, goal, neighbours, d, h)
83+
}
84+
85+
fun countAllShortcutsShorterThan(picoseconds: Int): Int =
86+
forEachCoordinates { pos, elem ->
87+
when {
88+
elem != wall -> null
89+
pos.isOnTheBorder() -> null
90+
pos.canNotBeCrossed() -> null
91+
else -> pos
92+
}
93+
}
94+
.filterNotNull()
95+
.map { coord ->
96+
setAt(coord, free)
97+
val pathWithShortcut = shortestPath()
98+
setAt(coord, wall)
99+
pathWithShortcut.distance
100+
}
101+
.filter { it <= picoseconds }
102+
.count()
103+
104+
private fun Coordinates.isOnTheBorder(): Boolean {
105+
return row == 0 || row == (height - 1) || col == 0 || col == width - 1
106+
}
107+
108+
private fun Coordinates.canNotBeCrossed(): Boolean {
109+
return (getAt(this + NORTH) == wall || getAt(this + SOUTH) == wall) &&
110+
(getAt(this + EAST) == wall || getAt(this + WEST) == wall)
111+
}
112+
113+
}

0 commit comments

Comments
 (0)