aoc_2024/day06.kts

135 lines
4.6 KiB
Plaintext
Raw Normal View History

2024-12-06 08:56:47 +00:00
#!/usr/bin/env kotlin
import java.io.File
import java.lang.RuntimeException
import java.util.Scanner
data class MoveResult(val newLocation: Pair<Int, Int>, val newDirection: Direction)
data class MoveHistory(val location: Pair<Int, Int>, val direction: Direction)
enum class Direction(val offset: Pair<Int, Int>) {
NORTH(Pair(0, -1)),
EAST(Pair(1, 0)),
SOUTH(Pair(0, 1)),
WEST(Pair(-1, 0));
fun move(currentLocation: Pair<Int, Int>): Pair<Int, Int> {
return Pair(
currentLocation.first + offset.first,
currentLocation.second + offset.second
)
}
fun turnLeft(): Direction {
return when (this) {
NORTH -> WEST
WEST -> SOUTH
SOUTH -> EAST
EAST -> NORTH
}
}
fun turnRight(): Direction {
return when (this) {
NORTH -> EAST
EAST -> SOUTH
SOUTH -> WEST
WEST -> NORTH
}
}
fun moveByPartOneRules(
currentLocation: Pair<Int, Int>,
blocks: Set<Pair<Int, Int>>
): MoveResult {
var currentDirection = this
do {
val potentialMove = currentDirection.move(currentLocation)
if (potentialMove !in blocks) {
return MoveResult(potentialMove, currentDirection)
}
currentDirection = currentDirection.turnRight()
} while (currentDirection != this)
throw RuntimeException("Nowhere to go! $currentLocation $this")
}
}
fun isPositionInTheRoom(player: Pair<Int, Int>, xMax: Int, yMax: Int): Boolean {
return player.first in 0..xMax && player.second in 0..yMax
}
val scanner = Scanner(System.`in`)
val visited = mutableSetOf<Pair<Int, Int>>()
val history = mutableListOf<MoveHistory>()
var playerLocation = Pair(0, 0)
var playerDirection: Direction = Direction.NORTH
val blocks = mutableSetOf<Pair<Int, Int>>()
val potentialBlocks = mutableSetOf<Pair<Int, Int>>()
var yMax = 0
var xMax = 0
while (scanner.hasNextLine()) {
val line = scanner.nextLine()
for (i in line.indices) {
if (line[i] == '.') {
continue
}
if (line[i] == '>') {
playerDirection = Direction.EAST
playerLocation = Pair(i, yMax)
} else if (line[i] == '<') {
playerDirection = Direction.WEST
playerLocation = Pair(i, yMax)
} else if (line[i] == '^') {
playerDirection = Direction.NORTH
playerLocation = Pair(i, yMax)
} else if (line[i] == 'v') {
playerDirection = Direction.SOUTH
playerLocation = Pair(i, yMax)
} else if (line[i] == '#') {
blocks.add(Pair(i, yMax))
}
}
xMax = xMax.coerceAtLeast(line.lastIndex)
yMax++
}
2024-12-06 09:31:20 +00:00
yMax-- // Should represent the max index.
2024-12-06 08:56:47 +00:00
val initialPosition = playerLocation.copy()
while (isPositionInTheRoom(playerLocation, xMax, yMax)) {
visited.add(playerLocation)
if (checkForLoopIfObstacleInFront(playerLocation, playerDirection, history)) {
potentialBlocks.add(playerDirection.move(playerLocation))
}
history.add(MoveHistory(playerLocation, playerDirection))
val (newLocation, newDirection) = playerDirection.moveByPartOneRules(playerLocation, blocks)
playerLocation = newLocation
playerDirection = newDirection
}
println("Player location: ${visited.size}")
println("Potential blocks: ${potentialBlocks.size}")
fun checkForLoopIfObstacleInFront(
playerLocation: Pair<Int, Int>,
playerDirection: Direction,
history: MutableList<MoveHistory>
): Boolean {
2024-12-06 09:31:20 +00:00
val potentialBlockLocation = playerDirection.move(playerLocation)
2024-12-06 08:56:47 +00:00
val nextMove = playerDirection.moveByPartOneRules(playerLocation, blocks)
2024-12-06 09:31:20 +00:00
val alreadyVisited = potentialBlockLocation in visited
val pathAlreadyBlocked = potentialBlockLocation != nextMove.newLocation
val positionNotInTheRoom = !isPositionInTheRoom(potentialBlockLocation, xMax, yMax)
if (alreadyVisited || pathAlreadyBlocked || positionNotInTheRoom) {
2024-12-06 08:56:47 +00:00
return false
}
var localLocation = playerLocation
var localDirection = playerDirection
val localHistory = history.toMutableList()
val localBlocks = blocks.toMutableSet()
2024-12-06 09:31:20 +00:00
localBlocks.add(potentialBlockLocation)
2024-12-06 08:56:47 +00:00
while (MoveHistory(localLocation, localDirection) !in localHistory) {
localHistory.add(MoveHistory(localLocation, localDirection))
val (newLocation, newDirection) = localDirection.moveByPartOneRules(localLocation, localBlocks)
localLocation = newLocation
localDirection = newDirection
if (!isPositionInTheRoom(localLocation, xMax, yMax)) {
return false
}
}
return true;
}