Skip to content
loading...

re: Advent of Code 2019 Solution Megathread - Day 13: Care Package VIEW POST

FULL DISCUSSION
 

Surprisingly simple! No modifications needed to the IntCode after the Robot problem, so just plug in the interpreter and tell it how to play the game!

Kotlin solution:

import arrow.core.Either
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
import arrow.core.getOrElse
import arrow.core.left
import arrow.core.right
import arrow.core.some
import intcode.CurrentState
import intcode.handleCodePoint
import intcode.toIntCodeProgram
import util.TwoD

data class PointL(
    override val x: Long,
    override val y: Long
) : TwoD<Long> {
    companion object
}

sealed class GameTile {
    object Empty : GameTile()
    object Wall : GameTile()
    object Block : GameTile()
    object Paddle : GameTile()
    object Ball : GameTile()
}

fun GameTile?.toGlyph() = when (this) {
    GameTile.Wall -> "▫️"
    GameTile.Block -> "🎁"
    GameTile.Paddle -> "πŸ“"
    GameTile.Ball -> "🏐"
    else -> "◾️"
}

fun Long.toGameTile(): GameTile {
    return when (this) {
        0L -> GameTile.Empty
        1L -> GameTile.Wall
        2L -> GameTile.Block
        3L -> GameTile.Paddle
        4L -> GameTile.Ball
        else -> throw Error("Bad GameTile index: $this")
    }
}


data class ArcadeGame(
    val code: MutableMap<Long, Long>,
    val screen: MutableMap<PointL, GameTile> = mutableMapOf(),
    val state: Either<String, CurrentState> = CurrentState().right(),
    var display: Option<Long> = Option.empty()
) {
    tailrec fun draw(): ArcadeGame {
        return when (state) {
            is Either.Left<String> -> this
            is Either.Right<CurrentState> -> when {
                state.b.output.size >= 3 -> {
                    drawSingleTile(state)
                    draw()
                }
                else -> this
            }
        }
    }

    private fun drawSingleTile(state: Either.Right<CurrentState>) =
        when (val p = PointL(state.b.output.pop(), state.b.output.pop())) {
            PointL(-1, 0) -> display = state.b.output.pop().some()
            else -> screen[p] = state.b.output.pop().toGameTile()
        }
}

private fun <K, V : Any> MutableMap<K, V>.findFirst(paddle: V): K = filterValues(paddle::equals).keys.first()

private fun Either<String, CurrentState>.withJoystickPosition(screen: MutableMap<PointL, GameTile>) = map {
    if (it.waitingForInput) {
        it.inputs.add(
            screen.findFirst(GameTile.Ball).x
                .compareTo(screen.findFirst(GameTile.Paddle).x).toLong()
        )
    }
    it
}

object Day13 {
    private tailrec fun ArcadeGame.play(): Either<String, ArcadeGame> {
        return when (state) {
            is Either.Left<String> -> state
            is Either.Right<CurrentState> -> when (state.b.pointer) {
                is None -> right()
                is Some<Long> -> {
                    printScreen()
                    copy(
                        state = handleCodePoint(code, state.withJoystickPosition(screen))
                    ).draw().play()
                }
                else -> "Unknown error.".left()
            }
        }
    }

    private const val FILENAME = "src/main/resources/day13.txt"
    private val fileData = FILENAME.toIntCodeProgram()


    fun part1() {
        val game = ArcadeGame(code = fileData.toMutableMap())
        val finished = game.play()
        println(
            finished.fold({ "Problem: $it" }, { it.screen.filterValues { v -> v is GameTile.Block }.count() })
        )
    }

    fun part2() {
        val game = ArcadeGame(code = fileData.toMutableMap().apply { this[0] = 2 })
        val finished = game.play()
        println(
            finished.fold(
                { "Problem: $it" },
                { it.display.getOrElse { "No score displayed." } }
            )
        )
    }
}

fun main() {
    Day13.part1()
    Day13.part2()
}

fun ArcadeGame.printScreen() {
    val topLeft = PointL(
        screen.keys.map(PointL::x).min() ?: 0L,
        screen.keys.map(PointL::y).min() ?: 0L
    )
    val bottomRight = PointL(
        screen.keys.map(PointL::x).max() ?: 0L,
        screen.keys.map(PointL::y).max() ?: 0L
    )
    println(
        (topLeft.y..bottomRight.y).joinToString("\n") { y ->
            (topLeft.x..bottomRight.x).joinToString("") { x ->
                screen[PointL(x, y)].toGlyph()
            }
        }
    )
}

code of conduct - report abuse