Skip to content

Commit 1c81e6d

Browse files
committed
Solve day 15
1 parent 157bb13 commit 1c81e6d

File tree

3 files changed

+256
-1
lines changed

3 files changed

+256
-1
lines changed

src/main/kotlin/day15/Day15.kt

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package day15
2+
3+
import helper.Debug
4+
import helper.point.Direction
5+
import helper.point.base.Point
6+
import helper.point.base.get
7+
import helper.point.base.indexOf
8+
import helper.point.base.points
9+
import helper.removeFirst
10+
import kotlin.collections.component1
11+
import kotlin.collections.component2
12+
13+
fun solveA(text: String, debug: Debug = Debug.Disabled): Int {
14+
val (mapText, instructionsText) = text.split("\n\n")
15+
val map = mapText.lines()
16+
val instructions = instructionsText.replace("\n", "")
17+
val walls = mutableSetOf<Point>()
18+
val boxes = mutableSetOf<Point>()
19+
var robot = map.indexOf('@')
20+
21+
map.points().forEach { p ->
22+
val c = map[p]
23+
when (c) {
24+
'#' -> walls += p
25+
'O' -> boxes += p
26+
}
27+
}
28+
29+
30+
instructions.forEach { c ->
31+
val direction = when (c) {
32+
'<' -> Direction.LEFT
33+
'>' -> Direction.RIGHT
34+
'^' -> Direction.UP
35+
'v' -> Direction.DOWN
36+
else -> TODO("Unknown direction $c")
37+
}
38+
39+
val nextPoint = robot + direction.point
40+
if (nextPoint !in walls && nextPoint !in boxes) {
41+
robot = nextPoint
42+
} else {
43+
val boxesToMove = mutableSetOf<Point>()
44+
var nextBox = nextPoint
45+
while (nextBox in boxes) {
46+
boxesToMove.add(nextBox)
47+
nextBox += direction.point
48+
}
49+
if (nextBox !in walls) {
50+
boxes.removeAll(boxesToMove)
51+
boxes.addAll(boxesToMove.map { it + direction.point })
52+
robot = nextPoint
53+
}
54+
}
55+
}
56+
57+
return boxes.sumOf { (x, y) -> x + y * 100 }
58+
}
59+
60+
61+
fun solveB(text: String, debug: Debug = Debug.Disabled): Int {
62+
val (mapText, instructionsText) = text.split("\n\n")
63+
val map = mapText.lines()
64+
val width = map[0].length * 2
65+
val height = map.size
66+
val instructions = instructionsText.replace("\n", "")
67+
val walls = mutableSetOf<Point>()
68+
val boxes = mutableSetOf<Pair<Point, Point>>()
69+
70+
var robot = map.indexOf('@').let { (x, y) -> Point(x * 2, y) }
71+
72+
map.points().forEach { p ->
73+
val p1 = Point(p.x * 2, p.y)
74+
val p2 = Point(p.x * 2 + 1, p.y)
75+
val c = map[p]
76+
when (c) {
77+
'#' -> {
78+
walls += p1
79+
walls += p2
80+
}
81+
82+
'O' -> {
83+
boxes.add(p1 to p2)
84+
}
85+
}
86+
}
87+
88+
debug {
89+
println("Initial State:")
90+
printMap(height, width, robot, walls, boxes)
91+
}
92+
93+
instructions.forEachIndexed { i, c ->
94+
val direction = when (c) {
95+
'<' -> Direction.LEFT
96+
'>' -> Direction.RIGHT
97+
'^' -> Direction.UP
98+
'v' -> Direction.DOWN
99+
else -> throw IllegalArgumentException("Unknown direction $c")
100+
}
101+
102+
val boxesToMove = mutableSetOf<Pair<Point, Point>>()
103+
val pointsToVisit = mutableSetOf(robot + direction.point)
104+
while (pointsToVisit.isNotEmpty() && pointsToVisit.first() !in walls) {
105+
val nextBoxPoint = pointsToVisit.removeFirst()
106+
val possibleBoxes = setOf(
107+
Pair(nextBoxPoint + Direction.LEFT.point, nextBoxPoint),
108+
Pair(nextBoxPoint, nextBoxPoint + Direction.RIGHT.point)
109+
)
110+
val box = possibleBoxes.firstOrNull { it in boxes }
111+
if (box != null) {
112+
boxesToMove.add(box)
113+
when (direction) {
114+
Direction.UP, Direction.DOWN -> {
115+
pointsToVisit.add(box.first + direction.point)
116+
pointsToVisit.add(box.second + direction.point)
117+
}
118+
119+
Direction.LEFT -> pointsToVisit.add(box.first + direction.point)
120+
else -> pointsToVisit.add(box.second + direction.point)
121+
}
122+
}
123+
}
124+
if (pointsToVisit.none { it in walls }) {
125+
boxes.removeAll(boxesToMove)
126+
boxes.addAll(boxesToMove.map { (first, second) -> first + direction.point to second + direction.point })
127+
robot += direction.point
128+
}
129+
debug {
130+
println("Movement $c ($i):")
131+
printMap(height, width, robot, walls, boxes)
132+
}
133+
}
134+
135+
return boxes.map { it.first }.sumOf { (x, y) -> x + y * 100 }
136+
}
137+
138+
private fun printMap(
139+
height: Int,
140+
width: Int,
141+
robot: Point,
142+
walls: Set<Point>,
143+
boxes: Set<Pair<Point, Point>>
144+
) {
145+
repeat(height) { y ->
146+
repeat(width) { x ->
147+
val point = Point(x, y)
148+
val boxL = Pair(point, point + Direction.RIGHT.point)
149+
val boxR = Pair(point + Direction.LEFT.point, point)
150+
when {
151+
point == robot -> print('@')
152+
point in walls -> print('#')
153+
boxL in boxes -> print('[')
154+
boxR in boxes -> print(']')
155+
else -> print('.')
156+
}
157+
}
158+
println()
159+
}
160+
println()
161+
}

src/main/resources/inputs

src/test/kotlin/day15/Day15KtTest.kt

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package day15
2+
3+
import helper.Debug
4+
import helper.readDayFile
5+
import kotlin.test.Test
6+
import kotlin.test.assertEquals
7+
8+
9+
internal class Day15KtTest {
10+
11+
private val day = 15
12+
13+
@Test
14+
fun sample1() {
15+
val text = """
16+
##########
17+
#..O..O.O#
18+
#......O.#
19+
#.OO..O.O#
20+
#..O@..O.#
21+
#O#..O...#
22+
#O..O..O.#
23+
#.OO.O.OO#
24+
#....O...#
25+
##########
26+
27+
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
28+
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
29+
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
30+
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
31+
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
32+
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
33+
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
34+
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
35+
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
36+
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
37+
""".trimIndent().trimEnd()
38+
39+
assertEquals(10092, solveA(text, Debug.Disabled))
40+
assertEquals(9021, solveB(text, Debug.Disabled))
41+
}
42+
43+
@Test
44+
fun sample2() {
45+
// val text = readDayFile(day, "sample2.in").readText().trimEnd()
46+
47+
val text = """
48+
########
49+
#..O.O.#
50+
##@.O..#
51+
#...O..#
52+
#.#.O..#
53+
#...O..#
54+
#......#
55+
########
56+
57+
<^^>>>vv<v>>v<<
58+
""".trimIndent().trimEnd()
59+
60+
assertEquals(2028, solveA(text, Debug.Disabled))
61+
}
62+
63+
@Test
64+
fun sample3() {
65+
// val text = readDayFile(day, "sample2.in").readText().trimEnd()
66+
67+
val text = """
68+
#######
69+
#...#.#
70+
#.....#
71+
#..OO@#
72+
#..O..#
73+
#.....#
74+
#######
75+
76+
<vv<<^^<<^^
77+
""".trimIndent().trimEnd()
78+
79+
assertEquals(618, solveB(text, Debug.Enabled))
80+
}
81+
82+
@Test
83+
fun solve() {
84+
val lines = readDayFile(day, "input").readText().trimEnd()
85+
86+
val solveA = solveA(lines)
87+
println("A: $solveA")
88+
assertEquals(1552879, solveA)
89+
90+
val solveB = solveB(lines)
91+
println("B: $solveB")
92+
assertEquals(1561175, solveB)
93+
}
94+
}

0 commit comments

Comments
 (0)