Dnes se mi povedla pouze část A. I přestože mi část B fungovala pro jejich příklad správně, konečný výsledek z nějakého důvodu správný nebyl – netuším proč, ale screw it 🙂 .
EDIT: Po poradě na Kotlin Slacku jsme přišli na to, že rozbourané vozíky musím odstranit ihned po kolizi ještě v rámci daného “ticku”. Řešení níže je nyní již opraveno.
import com.google.common.collect.Iterators
import com.ichipsea.kotlin.matrix.*
import com.svetylkovo.adventofcode2018.day13.Direction.*
import com.svetylkovo.adventofcode2018.toNonSeparatedPrettyString
import java.io.File
const val CORNER_TILT_RIGHT = '/'
const val CORNER_TILT_LEFT = '\\'
const val TRACK_HORIZONTAL = '-'
const val TRACK_VERTICAL = '|'
const val TRACK_INTERSECTION = '+'
const val TRACK_COLLISION = 'X'
const val EMPTINESS = ' '
enum class Direction(
val directionCharacter: Char,
val vector: IntArray
) {
UP('^', intArrayOf(0, -1)),
DOWN('v', intArrayOf(0, 1)),
LEFT('<', intArrayOf(-1, 0)),
RIGHT('>', intArrayOf(1, 0));
fun swapVector() = vector.reversedArray()
fun swapVectorInverted() = vector.reversedArray().map { it * -1 }.toIntArray()
fun getNewDirection(trackCharacter: Char, nextTurnProvider: () -> Turn): Direction {
return when (trackCharacter) {
CORNER_TILT_RIGHT ->
when (this) {
in UP..DOWN -> turnRight()
else -> turnLeft()
}
CORNER_TILT_LEFT -> when (this) {
in UP..DOWN -> turnLeft()
else -> turnRight()
}
TRACK_INTERSECTION -> when (nextTurnProvider()) {
Turn.RIGHT -> turnRight()
Turn.LEFT -> turnLeft()
else -> this
}
else -> this
}
}
internal fun turnLeft(): Direction {
val newVector = if (this in UP..DOWN) swapVector() else swapVectorInverted()
return byVector(newVector)
}
internal fun turnRight(): Direction {
val newVector = if (this in UP..DOWN) swapVectorInverted() else swapVector()
return byVector(newVector)
}
companion object {
fun parseFrom(value: Char) = values().find { it.directionCharacter == value }
?: throw Exception("Couldn't find Direction for $value")
fun byVector(vector: IntArray) = values().find { it.vector.contentEquals(vector) }
?: throw Exception("Couldn't find Direction for $vector")
}
}
val directionCharacters = values().map { it.directionCharacter }
enum class Turn {
LEFT, STRAIGHT, RIGHT
}
data class Cart(var x: Int, var y: Int, var direction: Direction) {
val nextTurn: Iterator<Turn> = Iterators.cycle(Turn.values().asIterable())
var collided = false
fun move() {
direction.run {
x += vector[0]
y += vector[1]
}
}
fun tick(map: Matrix<Char>) {
move()
direction = direction.getNewDirection(map[x, y]) { nextTurn.next() }
}
}
class CartTicker(var map: Matrix<Char>) {
constructor(rawInput: String) : this(rawInput.run {
val lines = lines()
val width = lines.map { it.trim() }.maxBy { it.length }?.length ?: 0
val height = lines.size
lines.map { it.padEnd(width, EMPTINESS) }
.flatMap { it.toList() }
.toMutableMatrix(width, height)
})
val carts = map.mapIndexed { x, y, value ->
when (value) {
in directionCharacters -> Cart(x, y, Direction.parseFrom(value))
else -> null
}
}.toList().filterNotNull().toMutableList()
init {
//replace carts with track symbols in the map
map = map.map {
if (it in directionCharacters) {
val direction = Direction.parseFrom(it)
when (direction) {
UP, DOWN -> TRACK_VERTICAL
LEFT, RIGHT -> TRACK_HORIZONTAL
}
} else it
}
}
val collisions = mutableMapOf<Pair<Int, Int>, MutableList<Cart>>()
val collidedCarts = mutableListOf<Cart>()
fun tick() {
carts.asSequence()
.filterNot { it.collided }
.sortedWith(compareBy({ it.y }, { it.x }))
.forEach {
it.tick(map)
detectCollisions()
if (collisions.isNotEmpty()) {
val newlyCollidedCarts = collisions.values.flatten()
collidedCarts+= newlyCollidedCarts
carts -= newlyCollidedCarts
collisions.clear()
}
}
}
private fun detectCollisions() {
carts.groupBy { it.x to it.y }
.filterValues { it.size > 1 && it.any { !it.collided } }
.forEach {
val coords = it.key
it.value.forEach { it.collided = true }
collisions.computeIfAbsent(coords) { mutableListOf() }.addAll(it.value)
}
}
fun cartsPosition() = map.mapIndexed { x, y, value ->
carts.find { it.x == x && it.y == y }?.direction?.directionCharacter ?: value
}.toNonSeparatedPrettyString()
}
fun main() {
val input = File("input13.txt").readText()
val tickerA = CartTicker(input)
while (tickerA.collidedCarts.isEmpty()) {
tickerA.tick()
}
//A
println("First collision: ${tickerA.collidedCarts.first()}")
//B
val tickerB = CartTicker(input)
while (tickerB.carts.size > 1) {
tickerB.run {
tick()
}
}
println("Last cart standing: ${tickerB.carts.single()}")
}