Jon Bristow

Posted on

# Advent of Code 2019 Solution Megathread - Day 13: Care Package

It's time to break out our IntCode interpreter and do something useful for once: play breakout.

### Day 13 - The Problem

Man, flying through the void is really boring. Good thing those elves are looking out for us and sent us some in-flight entertainment. It seems to be a rudimentary breakout clone. Bounce the ball off your paddle to break all the bricks.

In order to check the status of the code, Part 1 asks us to verify that we see the proper number of blocks on the screen when the game starts.

Part 2 is to actually play the game and report our high score!

### Ongoing Meta

If you were part of Ryan Palo's leaderboard last year, you're still a member of that!

I'll edit in any leaderboards that people want to post, along with any description for the kinds of people you want to have on it. (My leaderboard is being used as my office's leaderboard.) And if I get something wrong, please call me out or message me and I’ll fix it ASAP.

There's no limit to the number of leaderboards you can join, so there's no problem belonging to a "Beginner" and a language specific one if you want.

#### Neat Statistics

I'm planning on adding some statistics, but other than "what languages did we see yesterday" does anyone have any ideas?

##### Languages Seen On Day 12

Under construction

Jon Bristow • Edited

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 Ball : GameTile()
}

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

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

val code: MutableMap<Long, Long>,
val screen: MutableMap<PointL, GameTile> = mutableMapOf(),
val state: Either<String, CurrentState> = CurrentState().right(),
var display: Option<Long> = Option.empty()
) {
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) {
screen.findFirst(GameTile.Ball).x
)
}
it
}

object Day13 {
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()
}

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()
}
}
)
}

``````

Massimo Artizzu

This was much easier indeed!

## Part One

The only thing I've given for granted is that the machine, on the first part, never overwrites a tile, but I was ready to track that down eventually. I'm reporting just the relevant parts in JavaScript:

``````const game = createProgramInstance(codes, 1);
let blocks = 0;
while (true) {
const { value: x } = game.next();
if (typeof x === 'undefined') {
break;
}
game.next();
const { value: draw } = game.next();
if (draw === 2) {
blocks++;
}
}
console.log(blocks);
``````

## Part Two

I don't know if you noticed the hidden message in the text: "You do have crew quarters, but they won't fit in the machine." 😂
Anyway, after the initial confusion about "how to play this game?!", I realized this is just Arkanoid/Breakout! 😄 I just have to move the paddle left and right to reach the ball.
The only change I did to the main routing was to set a fixed input value instead of a stack of values (`joystickPosition`).

``````const game = createProgramInstance(codes, 1);
let score;
let ballX;
while (true) {
const { value: x } = game.next();
if (typeof x === 'undefined') {
break;
}
const { value: y } = game.next();
const { value } = game.next();
if (x === -1 && y === 0) {
score = value;
} else if (value === 3) {
} else if (value === 4) {
ballX = x;
}
}
console.log(score);
``````

Get my input at my repo.

Neil Gall

Pretty straightforward today, which was nice after yesterday's tricky part 2 that I haven't solved yet (I didn't have as much time as usual). I adapted the idea from the painting robot to screen drawing, and used a very simple input logic for part 2 to make the paddle track the ball's x coordinate.

``````opcode screen_input(void *io_context) {
struct screen *screen = (struct screen *)io_context;

return 1;
return -1;
else
return 0;
}
``````

Job done!

Johnny

Clojure solution:

``````(load-file "intcode.clj")

(ns Day13
(:require intcode))

; Adds or updates a tile in the provided map "tiles" given the 3-size output sequence in the format of (x y tile-type)
(defn assoc-tile [tiles tile-output]
(let [x (nth tile-output 0)
y (nth tile-output 1)
type (nth tile-output 2)]
(assoc tiles [x y] type)))

; Takes all outputs from a process state and updates the given map of tiles with them.
(defn update-tiles [tiles state]
(reduce assoc-tile tiles (partition 3 (:outputs state))))

; Loads the game with no quarter provided and returns a map of all tiles that are created.
(let [result (intcode/run code)]
(update-tiles {} result)))

; Counts the amount of tiles of the given type in a tiles map
(defn count-tiles [tiles tile-type]
(count (filter #{tile-type} (vals tiles))))

; In a map of tiles, finds the first key that is associated with the given tile type.
(defn find-tile-position [tiles tile-type]
(first (filter #(= tile-type (tiles %)) (keys tiles))))

; Plays and beats the game, returns the score at the end.
(defn play-game [code]
(loop [state (intcode/run (assoc code 0 2))
tiles (update-tiles {} state)]
(if (:terminated state)
(tiles [-1 0])
(let [ball-position (find-tile-position tiles 4)
joystick-tilt (compare (ball-position 0) (paddle-position 0))
next-state (intcode/continue state joystick-tilt)]
(recur next-state (update-tiles tiles next-state))))))

(def input (intcode/parse-intcodes (slurp (first *command-line-args*))))
(println "Number of block tiles:" (count-tiles (load-game input) 2))
(println "Game score:" (play-game input))
``````

Rizwan

No change in OpCode computer and it runs smoothly now.

Swift solution here

``````enum Joystick: Int {
case left   = -1
case right  = 1
case neutral = 0
}

enum TileId: Int, CustomStringConvertible {
var description: String {
get {
desc()
}
}

func desc() -> String {
switch self {
case .empty:
return "Empty"
case .wall:
return "Wall"
case .block:
return "Block"
case .ball:
return "Ball"
}
}

case empty = 0
case wall = 1
case block = 2
case ball = 4

}

struct Point: CustomStringConvertible, Equatable, Hashable {
var x: Int
var y: Int

var description: String {
get {
return "X: \(self.x) Y: \(self.y)"
}
}
}

var grid: [Point: TileId] = [:]
var memory: [Int]

init(_ memory: [Int]) {
self.memory = memory
}

func compute() {
let computer = Opcode(memory,0)

while !computer.done {
let x = computer.run()
let y = computer.run()
let id = TileId(rawValue: computer.run())

grid[Point.init(x: x, y: y)] = id
}
}

func getNumberOfTiles(for tile: TileId) -> Int {
return grid.values.filter { \$0 == tile }.count
}

func setQuarters(_ value: Int) {
memory[0] = value
}

func beat() -> Int {
let computer = Opcode(memory,0)
var score = 0
var ball = Point.init(x: 0, y: 0)
var paddle = Point.init(x: 0, y: 0)

while !computer.done {
let x = computer.run()
let y = computer.run()
let value = computer.run()

if x == -1 && y == 0 {
score = value
}
else {
let pos = Point.init(x: x, y: y)
if value == 4 {
ball = pos
}
if value == 3 {
}
}

computer.inputIds.append(Joystick.right.rawValue)
}
else if paddle.x > ball.x {
computer.inputIds.append(Joystick.left.rawValue)
}
else {
computer.inputIds.append(Joystick.neutral.rawValue)
}
}
return score
}
}

func partOne() {