### re: AoC Day 13: Mine Cart Madness VIEW POST

I promise I'll post my code eventually, I spent too long working on this animation of part1: gfycat.com/EasygoingMassiveHog

Oof, I played with this too long.

Here's it all.

### Kotlin Solution

private fun answer1(input: List<String>) =
step(0, input.findCarts(), input)

demolitionStep(0, input.findCarts(), input)

tailrec fun step(
i: Int,
carts: List<Cart>,
tracks: List<String>
): Point {
val (nextCarts, collisions) =
moveCarts(carts = carts.sorted(), tracks = tracks)

return when {
collisions.isEmpty() -> step(i + 1, nextCarts, tracks)
else -> collisions.first().loc
}
}

tailrec fun moveCarts(
carts: List<Cart>,
moved: List<Cart> = emptyList(),
collided: List<Cart> = emptyList(),
tracks: List<String>
): Pair<List<Cart>, List<Cart>> {

if (carts.isEmpty()) return moved to collided

val (ucRem, cRem) = carts.tail.splitBy { it.loc == h.loc }
val (ucMoved, cMoved) = moved.splitBy { it.loc == h.loc }

return when {
cRem.isEmpty() && cMoved.isEmpty() ->
moveCarts(ucRem, ucMoved + h, collided, tracks)
cRem.isEmpty() ->
moveCarts(ucRem, ucMoved, collided + h + cMoved, tracks)
cMoved.isEmpty() ->
moveCarts(ucRem, ucMoved, collided + h + cRem, tracks)
else ->
moveCarts(ucRem, ucMoved, collided + h + cRem + cMoved, tracks)
}

}

tailrec fun demolitionStep(
i: Int,
carts: List<Cart>,
tracks: List<String>
): Point {
val (nextCarts, collisions) = moveCarts(
carts = carts.sorted(),
tracks = tracks
)
return when {
carts.count() == 1 -> carts.first().loc
else -> demolitionStep(i + 1, nextCarts, tracks)
}
}

enum class Choice {
LEFT {
override fun makeChoice(cart: Cart) = when (cart.direction) {
Direction.RIGHT -> cart.nextPosition(Direction.UP, Choice.LEFT)
Direction.LEFT -> cart.nextPosition(Direction.DOWN, Choice.LEFT)
Direction.UP -> cart.nextPosition(Direction.LEFT, Choice.LEFT)
Direction.DOWN -> cart.nextPosition(Direction.RIGHT, Choice.LEFT)
}
},
RIGHT {
override fun makeChoice(cart: Cart) = when (cart.direction) {
Direction.LEFT -> cart.nextPosition(Direction.UP, Choice.RIGHT)
Direction.RIGHT -> cart.nextPosition(Direction.DOWN, Choice.RIGHT)
Direction.UP -> cart.nextPosition(Direction.RIGHT, Choice.RIGHT)
Direction.DOWN -> cart.nextPosition(Direction.LEFT, Choice.RIGHT)
}
},
STRAIGHT {
override fun makeChoice(cart: Cart) =
when (cart.direction) {
Direction.RIGHT, Direction.LEFT ->
cart.nextPosition(cart.direction, Choice.STRAIGHT)
Direction.DOWN, Direction.UP ->
cart.nextPosition(cart.direction, Choice.STRAIGHT)
}
};

abstract fun makeChoice(cart: Cart): Cart
}

enum class Direction(val char: Char) {
UP('^') {
override fun turnBack() = LEFT
override fun turnForward() = RIGHT
override fun move(loc: Point) = Point(loc.x, loc.y - 1)
},
DOWN('v') {
override fun turnBack() = RIGHT
override fun turnForward() = LEFT
override fun move(loc: Point) = Point(loc.x, loc.y + 1)
},
LEFT('<') {
override fun turnBack() = UP
override fun turnForward() = DOWN
override fun move(loc: Point) = Point(loc.x - 1, loc.y)
},
RIGHT('>') {
override fun turnBack() = DOWN
override fun turnForward() = UP
override fun move(loc: Point) = Point(loc.x + 1, loc.y)
};

abstract fun move(loc: Point): Point
abstract fun turnBack(): Direction
abstract fun turnForward(): Direction

override fun toString(): String = this.char.toString()
}

fun Char.toDirection(): Direction? {
return Direction.values().find { it.char == this }
}

fun <E> Direction?.whenNotNull(function: (Direction) -> E): E? = when {
this != null -> function(this)
else -> null
}

class OffTheRailsException(cart: Point) : Exception("Off the rails! \$cart")

data class Cart(
val loc: Point,
val direction: Direction,
val lastChoice: Choice?,
val id: Int
) : Comparable<Cart> {
override fun compareTo(other: Cart) =
when (val ycomp = y.compareTo(other.y)) {
0 -> x.compareTo(other.x)
else -> ycomp
}

constructor(loc: Point, direction: Direction, lastChoice: Choice?) : this(
loc,
direction,
lastChoice,
loc.hashCode() + direction.hashCode()
)

}

fun Cart.nextPosition(direction: Direction, lastChoice: Choice?) =
Cart(direction.move(loc), direction, lastChoice, id)

fun Cart.nextPosition(direction: Direction) =
Cart(direction.move(loc), direction, lastChoice, id)

// '\'
fun Cart.turnBackCorner() = nextPosition(direction.turnBack())

// '/'
fun Cart.turnForwardCorner() = nextPosition(direction.turnForward())

val Cart.x: Int get() = loc.x
val Cart.y: Int get() = loc.y

fun Cart.move(tracks: List<String>) = when (tracks[y][x]) {
'|', '^', 'v' -> nextPosition(
direction
)
'-', '<', '>' -> nextPosition(
direction
)
'\\' -> turnBackCorner()
'/' -> turnForwardCorner()
'+' -> intersection()
else -> throw OffTheRailsException(loc)
}

fun Cart.intersection() = when (lastChoice) {
null, Choice.RIGHT -> Choice.LEFT.makeChoice(this)
Choice.STRAIGHT -> Choice.RIGHT.makeChoice(this)
Choice.LEFT -> Choice.STRAIGHT.makeChoice(this)
}

fun <E> List<E>.splitBy(predicate: (E) -> Boolean) =
groupBy(predicate).let {
(it[false] ?: emptyList()) to (it[true] ?: emptyList())
}

fun List<String>.findCarts() =
mapIndexed { y, xl ->
xl.mapIndexedNotNull { x, c ->
c.toDirection().whenNotNull {
Cart(Point(x, y), it, null)
}
}
}.flatten()

code of conduct - report abuse