Skip to content

Commit

Permalink
Finished 2024 day 15 both parts
Browse files Browse the repository at this point in the history
  • Loading branch information
tymscar committed Dec 15, 2024
1 parent 6f54442 commit 27fa419
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 2 deletions.
2 changes: 2 additions & 0 deletions 2024/kotlin/src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.tymscar.day11.solve as day11
import com.tymscar.day12.solve as day12
import com.tymscar.day13.solve as day13
import com.tymscar.day14.solve as day14
import com.tymscar.day15.solve as day15

fun main() {
day01()
Expand All @@ -30,4 +31,5 @@ fun main() {
day12()
day13()
day14()
day15()
}
83 changes: 82 additions & 1 deletion 2024/kotlin/src/main/kotlin/com/tymscar/day15/part1/part1.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,86 @@
package com.tymscar.day15.part1


private enum class EntityType { WALL, ROBOT, BOX, EMPTY }

private enum class Move { UP, DOWN, LEFT, RIGHT }

private data class Position(val x: Int, val y: Int) {
fun getNextPosition(move: Move): Position = when (move) {
Move.UP -> Position(x - 1, y)
Move.DOWN -> Position(x + 1, y)
Move.LEFT -> Position(x, y - 1)
Move.RIGHT -> Position(x, y + 1)
}
}

private typealias Map = MutableMap<Position, EntityType>

private data class State(val map: Map) {
private fun getRobotPosition(): Position = map.filterValues { it == EntityType.ROBOT }.keys.first()
fun getEntitiesToMove(direction: Move): List<Position> {
val entities = mutableListOf<Position>()
var currPosition = getRobotPosition()
if (map[currPosition.getNextPosition(direction)] == EntityType.WALL) return emptyList()
while (map[currPosition] == EntityType.BOX || map[currPosition] == EntityType.ROBOT) {
entities.add(currPosition)
currPosition = currPosition.getNextPosition(direction)
}
return if (map[currPosition] == EntityType.EMPTY) entities.reversed() else emptyList()
}

fun applyMove(move: Move) {
val toMove = getEntitiesToMove(move)
if (toMove.isEmpty()) return

toMove.forEach { map[it.getNextPosition(move)] = map[it] as EntityType }
toMove.last().let { map[it] = EntityType.EMPTY }
}
}


private fun getState(input: List<String>): State {
var map: MutableMap<Position, EntityType> = mutableMapOf()
for (x in input.indices) {
for (y in input[x].indices) {
val position = Position(x, y)
when (input[x][y]) {
'#' -> map[position] = EntityType.WALL
'@' -> map[position] = EntityType.ROBOT
'O' -> map[position] = EntityType.BOX
'.' -> map[position] = EntityType.EMPTY
else -> throw IllegalArgumentException("Invalid entity")
}
}
}
return State(map)
}

private fun getMoves(input: List<String>): List<Move> = input.joinToString("")
.map {
when (it) {
'^' -> Move.UP
'v' -> Move.DOWN
'<' -> Move.LEFT
'>' -> Move.RIGHT
else -> throw IllegalArgumentException("Invalid move")
}
}


fun solve(input: String): String {
return input
val (mapInput, movesInput) = input.split("\n\n").map(String::lines)
val state = getState(mapInput)
val moves = getMoves(movesInput)

moves.forEach(state::applyMove)

return state
.map
.filter { it.value == EntityType.BOX }
.keys
.sumOf {
it.x * 100 + it.y
}
.toString()
}
170 changes: 169 additions & 1 deletion 2024/kotlin/src/main/kotlin/com/tymscar/day15/part2/part2.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,173 @@
package com.tymscar.day15.part2


private enum class EntityType { WALL, ROBOT, BOXL, BOXR, EMPTY }

private enum class Move { UP, DOWN, LEFT, RIGHT }

private data class Position(val x: Int, val y: Int) {
fun getNextPosition(move: Move): Position = when (move) {
Move.UP -> Position(x - 1, y)
Move.DOWN -> Position(x + 1, y)
Move.LEFT -> Position(x, y - 1)
Move.RIGHT -> Position(x, y + 1)
}

fun getPreviousPosition(move: Move): Position = when (move) {
Move.UP -> Position(x + 1, y)
Move.DOWN -> Position(x - 1, y)
Move.LEFT -> Position(x, y + 1)
Move.RIGHT -> Position(x, y - 1)
}

fun getRightPosition(): Position = Position(x, y + 1)
fun getLeftPosition(): Position = Position(x, y - 1)
}

private typealias Map = MutableMap<Position, EntityType>

private data class State(val map: Map) {
private fun getRobotPosition(): Position = map.filterValues { it == EntityType.ROBOT }.keys.first()

private fun getEntitiesToMoveVertical(direction: Move): List<Position> {
val initialRobotPosition = getRobotPosition()
val entities = mutableListOf<Position>()
if (map[initialRobotPosition.getNextPosition(direction)] == EntityType.WALL) return emptyList()
var currHorizon = listOf(initialRobotPosition)

while (true) {
val anyWalls = currHorizon.any { map[it.getNextPosition(direction)] == EntityType.WALL }
if (anyWalls) return emptyList()
entities.addAll(currHorizon)
val allEmpty = currHorizon.all { map[it.getNextPosition(direction)] == EntityType.EMPTY }
if (allEmpty) {
entities.addAll(
entities
.filter { map[it.getNextPosition(direction)] == EntityType.EMPTY }
.map { it.getNextPosition(direction) })
val result = entities.drop(1).sortedBy { it.x }
return if (direction == Move.DOWN) result.reversed() else result
}

currHorizon = currHorizon
.map { it.getNextPosition(direction) }
.filter { map[it] == EntityType.BOXL || map[it] == EntityType.BOXR }
.flatMap {
when (map[it]) {
EntityType.BOXL -> listOf(it, it.getRightPosition())
EntityType.BOXR -> listOf(it, it.getLeftPosition())
else -> throw IllegalArgumentException("Invalid entity")
}
}
.distinct()
}
}

private fun getEntitiesToMoveHorizontal(direction: Move): List<Position> {
val entities = mutableListOf<Position>()
var currPosition = getRobotPosition()
if (map[currPosition.getNextPosition(direction)] == EntityType.WALL) return emptyList()
while (map[currPosition] == EntityType.BOXR || map[currPosition] == EntityType.BOXL || map[currPosition] == EntityType.ROBOT) {
entities.add(currPosition)
currPosition = currPosition.getNextPosition(direction)
}
return if (map[currPosition] == EntityType.EMPTY) entities.reversed() else emptyList()
}

fun getEntitiesToMove(direction: Move): List<Position> = when (direction) {
Move.UP, Move.DOWN -> getEntitiesToMoveVertical(direction)
Move.LEFT, Move.RIGHT -> getEntitiesToMoveHorizontal(direction)
}

private fun applyMoveHorizontal(move: Move, toMove: List<Position>) {
toMove.forEach { map[it.getNextPosition(move)] = map[it] as EntityType }
toMove.last().let { map[it] = EntityType.EMPTY }
}

private fun applyMoveVertical(move: Move, toMove: List<Position>) {
val robotPosition = getRobotPosition()
toMove.forEach {
val previousPosition = it.getPreviousPosition(move)
val previousItem = map[previousPosition]
map[it] = if (!toMove.contains(previousPosition)) {
if (previousItem == EntityType.ROBOT) EntityType.ROBOT else EntityType.EMPTY
} else when (previousItem) {
EntityType.WALL -> EntityType.EMPTY
else -> previousItem
} as EntityType
}
map[robotPosition] = EntityType.EMPTY
}

fun applyMove(move: Move) {
val toMove = getEntitiesToMove(move)
if (toMove.isEmpty()) return

when (move) {
Move.UP, Move.DOWN -> applyMoveVertical(move, toMove)
Move.LEFT, Move.RIGHT -> applyMoveHorizontal(move, toMove)
}
}
}

private fun getState(input: List<String>): State {
var map: MutableMap<Position, EntityType> = mutableMapOf()
for (x in input.indices) {
for (y in input[x].indices) {
val positionLeft = Position(x, y * 2)
val positionRight = Position(x, y * 2 + 1)
when (input[x][y]) {
'#' -> {
map[positionLeft] = EntityType.WALL
map[positionRight] = EntityType.WALL
}

'@' -> {
map[positionLeft] = EntityType.ROBOT
map[positionRight] = EntityType.EMPTY
}

'O' -> {
map[positionLeft] = EntityType.BOXL
map[positionRight] = EntityType.BOXR
}

'.' -> {
map[positionLeft] = EntityType.EMPTY
map[positionRight] = EntityType.EMPTY
}

else -> throw IllegalArgumentException("Invalid entity")
}
}
}
return State(map)
}

private fun getMoves(input: List<String>): List<Move> = input.joinToString("")
.map {
when (it) {
'^' -> Move.UP
'v' -> Move.DOWN
'<' -> Move.LEFT
'>' -> Move.RIGHT
else -> throw IllegalArgumentException("Invalid move")
}
}

fun solve(input: String): String {
return input
val (mapInput, movesInput) = input.split("\n\n").map(String::lines)
val state = getState(mapInput)
val moves = getMoves(movesInput)

moves.forEach(state::applyMove)

return state
.map
.filter { it.value == EntityType.BOXL }
.keys
.sumOf {
it.x.toLong() * 100L + it.y.toLong()
}
.toString()
}

0 comments on commit 27fa419

Please sign in to comment.