Skip to content

Commit 252d8db

Browse files
committed
Solution Day 15, part two
1 parent 118f296 commit 252d8db

File tree

3 files changed

+179
-33
lines changed

3 files changed

+179
-33
lines changed
Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,10 @@ fun main() {
88
println("$day part 1")
99

1010
fun part1(input: List<String>): Int {
11-
val warehouse = Warehouse(input.takeWhile { it.isNotBlank() })
11+
val warehouse = NormalWarehouse(input.takeWhile { it.isNotBlank() })
1212
warehouse.printGrid()
1313

14-
val movements = input
15-
.takeLastWhile { it.isNotBlank() }
16-
.joinToString(separator = "")
17-
.map {
18-
when (it) {
19-
'^' -> Direction.NORTH
20-
'>' -> Direction.EAST
21-
'v' -> Direction.SOUTH
22-
'<' -> Direction.WEST
23-
else -> error("Unexpected direction Char: $it")
24-
}
25-
}
14+
val movements = input.toDay15Movements()
2615

2716
warehouse.moveRobot(movements)
2817
warehouse.printGrid()
@@ -53,15 +42,30 @@ fun main() {
5342
printAndCheck(input, ::part1, 1463715)
5443
}
5544

56-
private class Warehouse(input: List<String>) : Grid<Char>(input) {
57-
private val wall = '#'
58-
private val robot = '@'
59-
private val free = '.'
60-
private val goods = 'O'
45+
fun List<String>.toDay15Movements(): List<Direction> = takeLastWhile { it.isNotBlank() }
46+
.joinToString(separator = "")
47+
.map {
48+
when (it) {
49+
'^' -> Direction.NORTH
50+
'>' -> Direction.EAST
51+
'v' -> Direction.SOUTH
52+
'<' -> Direction.WEST
53+
else -> error("Unexpected direction Char: $it")
54+
}
55+
}
56+
57+
58+
abstract class Warehouse(input: List<String>) : Grid<Char>(input) {
59+
protected val wall = '#'
60+
protected val robot = '@'
61+
protected val free = '.'
6162

6263
override val nullElement = wall
6364
override fun Char.toElementType(): Char = this
6465

66+
protected abstract val leftGoodsChar: Char
67+
protected abstract fun tryToMove(from: Coordinates, direction: Direction, thing: Char): Boolean
68+
6569
fun moveRobot(movements: List<Direction>) {
6670
movements.fold(findTheRobot()) { position, direction ->
6771
if (tryToMove(position, direction, robot)) {
@@ -72,7 +76,25 @@ private class Warehouse(input: List<String>) : Grid<Char>(input) {
7276
}
7377
}
7478

75-
private fun tryToMove(from: Coordinates, direction: Direction, thing: Char): Boolean {
79+
private fun findTheRobot(): Coordinates = forEachElement { row, col, element ->
80+
if (element == robot) Coordinates(row, col) else null
81+
}.filterNotNull().first()
82+
83+
// GPS = Goods Positioning System
84+
fun sumGPSCoordinates(): Int = forEachElement { row, col, element ->
85+
if (element == leftGoodsChar) {
86+
100 * row + col
87+
} else {
88+
0
89+
}
90+
}.sum()
91+
}
92+
93+
private class NormalWarehouse(input: List<String>) : Warehouse(input) {
94+
private val goods = 'O'
95+
override val leftGoodsChar = goods
96+
97+
override fun tryToMove(from: Coordinates, direction: Direction, thing: Char): Boolean {
7698
val target = from + direction
7799
if (getAt(target) == free) {
78100
setAt(target, thing)
@@ -90,17 +112,4 @@ private class Warehouse(input: List<String>) : Grid<Char>(input) {
90112
}
91113
return false
92114
}
93-
94-
private fun findTheRobot(): Coordinates = forEachElement { row, col, element ->
95-
if (element == robot) Coordinates(row, col) else null
96-
}.filterNotNull().first()
97-
98-
fun sumGPSCoordinates(): Int = forEachElement { row, col, element ->
99-
if (element == goods) {
100-
100 * row + col
101-
} else {
102-
0
103-
}
104-
}.sum()
105-
106-
}
115+
}

src/main/kotlin/Day15_part2.kt

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import de.ronny_h.extensions.Coordinates
2+
import de.ronny_h.extensions.Direction
3+
import de.ronny_h.extensions.Direction.EAST
4+
import de.ronny_h.extensions.Direction.WEST
5+
6+
fun main() {
7+
val day = "Day15"
8+
9+
println("$day part 2")
10+
11+
fun part2(input: List<String>): Int {
12+
val widenedInput = input.widenTheWarehouseInput()
13+
val warehouse = WideWarehouse(widenedInput)
14+
warehouse.printGrid()
15+
16+
val movements = input.toDay15Movements()
17+
18+
warehouse.moveRobot(movements)
19+
warehouse.printGrid()
20+
21+
return warehouse.sumGPSCoordinates()
22+
}
23+
24+
printAndCheck(
25+
"""
26+
#######
27+
#...#.#
28+
#.....#
29+
#..OO@#
30+
#..O..#
31+
#.....#
32+
#######
33+
34+
<vv<<^^<<^^
35+
""".trimIndent().lines(),
36+
::part2, 618
37+
)
38+
39+
val testInput = readInput("${day}_test")
40+
val input = readInput(day)
41+
printAndCheck(testInput, ::part2, 9021)
42+
printAndCheck(input, ::part2, 1481392)
43+
}
44+
45+
private fun List<String>.widenTheWarehouseInput(): List<String> = takeWhile { it.isNotBlank() }
46+
.map {
47+
val chars = it.toMutableList()
48+
val listIterator = chars.listIterator()
49+
for (elem in listIterator) {
50+
when (elem) {
51+
'#' -> listIterator.add('#')
52+
'O' -> {
53+
listIterator.set('[')
54+
listIterator.add(']')
55+
}
56+
57+
'.' -> listIterator.add('.')
58+
'@' -> listIterator.add('.')
59+
}
60+
}
61+
chars.joinToString("")
62+
}
63+
64+
// a thing can be the robot, a wall or goods
65+
private data class ThingInWarehouse(val thing: Char, val from: Coordinates, val direction: Direction) {
66+
val to: Coordinates = from + direction
67+
}
68+
69+
private class WideWarehouse(input: List<String>) : Warehouse(input) {
70+
private val goods = listOf('[', ']')
71+
override val leftGoodsChar = goods.first()
72+
73+
override fun tryToMove(from: Coordinates, direction: Direction, thing: Char): Boolean {
74+
return tryToMove(setOf(ThingInWarehouse(thing, from, direction)))
75+
}
76+
77+
private val rightGoodsChar = goods.last()
78+
79+
private fun tryToMove(things: Set<ThingInWarehouse>): Boolean {
80+
val toMove = collectNeighbours(things)
81+
// println("--- try to move: $toMove")
82+
if (toMove.all { getAt(it.to) == free }) {
83+
toMove.forEach {
84+
setAt(it.to, it.thing)
85+
setAt(it.from, free)
86+
}
87+
// printGrid()
88+
return true
89+
}
90+
if (toMove.any { getAt(it.to) == wall }) {
91+
return false
92+
}
93+
check(toMove.all { getAt(it.to) in (goods + free) })
94+
if (tryToMove(toMove.targets())) {
95+
toMove.forEach {
96+
setAt(it.to, it.thing)
97+
setAt(it.from, free)
98+
}
99+
// printGrid()
100+
return true
101+
}
102+
return false
103+
}
104+
105+
/*
106+
[] []
107+
[][]
108+
[]
109+
@
110+
*/
111+
private fun collectNeighbours(things: Set<ThingInWarehouse>): Set<ThingInWarehouse> {
112+
if ((things.size == 1 && things.single().thing == robot) || things.first().direction.isHorizontal()) {
113+
return things
114+
}
115+
val targets = buildSet {
116+
things.forEach { thing ->
117+
if (thing.thing == leftGoodsChar) {
118+
add(thing)
119+
add(ThingInWarehouse(rightGoodsChar, thing.from + EAST, thing.direction))
120+
} else if (thing.thing == rightGoodsChar) {
121+
add(thing)
122+
add(ThingInWarehouse(leftGoodsChar, thing.from + WEST, thing.direction))
123+
}
124+
// here, things pointing to 'free' are filtered out
125+
}
126+
}
127+
return targets
128+
}
129+
130+
private fun Set<ThingInWarehouse>.targets() = map {
131+
it.copy(thing = getAt(it.to), from = it.to)
132+
}.toSet()
133+
134+
}

src/main/kotlin/de/ronny_h/extensions/Coordinates.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,7 @@ enum class Direction(val row: Int, val col: Int) {
3838
SOUTH -> ''
3939
WEST -> ''
4040
}
41+
42+
fun isHorizontal(): Boolean = this == EAST || this == WEST
43+
fun isVertical(): Boolean = this == NORTH || this == SOUTH
4144
}

0 commit comments

Comments
 (0)