DEV Community

Cover image for Care Package
Robert Mion
Robert Mion

Posted on

Care Package

Advent of Code 2019 Day 13

Try the simulator using your puzzle input!

Simulator of Block Breaker game

Task: Solve for X where...

Part 1

X = the number of block tiles on the screen when the game exits
Enter fullscreen mode Exit fullscreen mode

Part 2

X = the score after the last block has broken
Enter fullscreen mode Exit fullscreen mode

Example output

It's not of an Intcode program, but of output from some imaginary program

1,2,3,6,5,4
Enter fullscreen mode Exit fullscreen mode

It represents:

  • An Intcode program's output values
  • Where every third value is a type of tile
  • And every first and third value are coordinates relative to a top-left origin point

Part 1

  1. Intcode computer: Round 6!
  2. Confirming I see the outputs and the program halts
  3. Updating my computer's logic to handle each output value

Intcode computer: Round 6!

  • This seems like another fun puzzle that leverages my completed Intcode computer
  • In this puzzle, the program draws a game board by rendering empty or filled tiles
  • Part 1 seems like an easy 'gut check'
  • I anticipate Part 2 will have me render the board and maybe find some fastest path from a start to an end?

Confirming I see the outputs and the program halts

Without updating my opcode 4 instructions, and logging all output, I see an initial list like this:

0, 0, 1, 1, 0, 1, 2, 0, 1
Enter fullscreen mode Exit fullscreen mode

Success:

  • The program finishes
  • The output looks as expected

Updating my computer's logic to handle each output value

I need to update three things:

  1. Initialize a tiles array with five elements, each one an empty set of unique values
  2. Make the computer return the size of the third unique set of values - that corresponds to all tiles with id 2, block
  3. Update opcode 4 such that any time the list of outputs contains a number of items that is divisible by 3, add a new item to the set corresponding to the position signified by the last output value...where that item is equivalent to a stringified version of the values 3rd to last and 2nd to last in the list of outputs

In essence, the third thing does this:

Output list:
..., 2, 3, 4

Add to set at tiles location 4:
'2,3'
Enter fullscreen mode Exit fullscreen mode

Running my computer returns the correct answer:

  • the number of block tiles

When I change it to display the counts of each tile id, I notice:

  • Only 1 horizontal paddle tile
  • Only 1 ball
  • About 100 walls
  • About 300 blocks
  • About 700 empty spaces

I'm excited to render the game board!

Part 2

  1. The joystick confuses me
  2. Let's try rendering the game board!
  3. Seeing the board resolves some confusion about the joystick...and creates more
  4. Checking the first few turns of the game
  5. All the tests, setters and updaters I know of
  6. Updating the input
  7. Troubleshooting, debugging and scratching my head
  8. OMG! It worked!
  9. Building a Block Breaker game simulator

The joystick confuses me

What I feel confident about:

  • I understand the joystick can move left and right
  • I understand that based on its position, the program's input should change to either -1,0,1
  • I assume the joystick controls the horizontal position of the horizontal paddle

What I'm not sure about:

  • How will the computer or program track the position of the ball?
  • Does it even need to?
  • Isn't that how the computer will know to adjust the input?

Let's try rendering the game board!

  • I assumed that the dimensions of the board could be determined by finding the minimum and maximum x and y coordinates of just the wall tiles
  • After making some tweaks to the code I wrote which rendered the panels for Day 11's painting robot, I produced the output seen below, using a # to represent what I thought were walls
............................................
.###########################################
.##.#.##......#.#####.###..........####.##.#
.##..##..###..#.#.#####.##...#.#.#.#.#####.#
.##.#.####..##...#..###......##.#.#.....####
.#..###.#.##....#.#..####.###..###.#.####.##
.#...##..##.###..#######.###..#.#.###...#..#
.###....###.##.###....###########....#.#..##
.######....####...##.#.##.#.#.#.#####.##...#
.#...#.###.#.....#..#.##.#.###..#.##.#######
.##.#....##.##.###..##.########.#.###.####.#
.####.#..##..#.####.#..#.#...##.##.#...#####
.#.#.#...#....#.##.#.##...####..#####.######
.#.......#..#.##.##.######.#....#####...#..#
.##.##.##.#.####.#...###.##.#.#..##..#.##..#
.###..#...#.#.####......#.#.##.#..###.###..#
.#.##.##..#....##.##.##..##.####.###.....#.#
.#.######..##..##.##..###.#..##.#.#...######
.###########################################
.###################.#######################
.###########################################
.###########################################
.#####################.#####################
.###########################################
Enter fullscreen mode Exit fullscreen mode

That can't be right:

  • What's with the two seemingly unreachable openings near the bottom?
  • What's with the left and top offset?

I'm an idiot

  • I mistakenly rendered empty tiles as #s.
  • I also mistakenly used the open spaces to determine the boundaries of the game board

After updating my code to check all board tile coordinates to determine the boundaries, and updating the symbols used to render each tile type...
...the game board was rendered correctly as shown below:

.............................................
.                                           .
.  # #  ###### #     #   ##########    #  # .
.  ##  ##   ## # #     #  ### # # # #     # .
.  # #    ##  ### ##   ######  # # #####    .
. ##   # #  #### # ##    #   ##   # #    #  .
. ###  ##  #   ##       #   ## # #   ### ## .
.   ####   #  #   ####           #### # ##  .
.      ####    ###  # #  # # # #     #  ### .
. ### #   # ##### ## #  # #   ## #  #       .
.  # ####  #  #   ##  #        # #   #    # .
.    # ##  ## #    # ## # ###  #  # ###     .
. # # ### #### #  # #  ###    ##     #      .
. ####### ## #  #  #      # ####     ### ## .
.  #  #  # #    # ###   #  # # ##  ## #  ## .
.   ## ### # #    ###### # #  # ##   #   ## .
. #  #  ## ####  #  #  ##  #    #   ##### # .
. #      ##  ##  #  ##   # ##  # # ###      .
.                                           .
.                   O                       .
.                                           .
.                                           .
.                     =                     .
.                                           .
Enter fullscreen mode Exit fullscreen mode

Cool! It looks like the retro arcade game, Brick Breaker!

Seeing the board resolves some confusion about the joystick...and creates more

First, I overlooked this rule about the ball:

The ball moves diagonally and bounces off objects

Maybe this is what happens during gameplay:

  • The ball moves diagonally - up-right, up-left, down-right, down-left
  • The paddle needs to follow the ball horizontally
  • The position of the paddle is controlled by the joystick: if the paddle needs to move left, joystick instructs -1; move right, instruct 1; stay still, instruct 0

What now?

  • Based on the initial position of the ball and paddle, should I assume it is mid-fall? That seems presumptuous.
  • Maybe I should just use 0 as the initial input
  • Should I assume that I'll be told the position of the ball and paddle after each output of the current score...assuming the score updating because the ball bounced off of a block?
  • Perhaps I need to see a few lines of output after using 0 as the initial input and updating the value at memory address 0 to 2

Checking the first few turns of the game

Confirmed:

  • Changing the value at memory address 0 to 2 still generates the same output values that identify the placement of each game board tile
  • The first score output is 0

Each series of three values output after the first score is one of three possibilities

  1. Another score
  2. The position of the ball
  3. The position of an empty tile

It seems like the output toggles between possibilities 2 and 3, with an eventual 1, like this:

1,2,3,2,3,2,3,2,3,2,3,1
Enter fullscreen mode Exit fullscreen mode

That makes me sense that the ball is moving, bouncing off blocks, causing them to become empty tiles.

I assume that each time the output corresponds to the ball's position, I should update the input to reflect -1,0,1 based on whether the horizontal paddle is to the right, underneath, or left of the ball.

All the tests, setters and updaters I know of

Whenever the output has a length divisible by 3, that could mean the computer must do one of several things:

  • Account for new tile...as long as no scores have been recorded yet
  • Setup game board...as soon as the first score has been recorded - and all tiles have been accounted for
  • Render game board...only after setup, when the ball and paddle moved, or a block disappears and becomes an empty tile
  • Update position of ball, input and paddle...when the last value in output is a 4
  • Remove block tile and add empty tile...when the last value in output is a 0 - meaning a block tile has become an empty tile

Updating the input

I wrote a new function that updates the input and the position of the horizontal paddle on my game board:

Any time the ball moves:
  Extract the X position of the ball and the paddle
  Subtract the latter from the former
  If the difference is -1 or lower
    Update input to -1
  Else, if the difference is 0
    Update input to 0
  Else, if the difference is 1
    Update input to 1
  Update the X position of the paddle to the sum of the current position and the new input value
Enter fullscreen mode Exit fullscreen mode

Troubleshooting, debugging and scratching my head

  • It took some serious console.log()ing to dig myself out of some of the code I'd written moments earlier
  • Some of it was ensuring I referenced the correct location in the list of output values
  • Some of it was ensuring I referenced the single string inside the arrays representing the ball and paddle tiles
  • Some of it was ensuring my tests checked the proper conditions to run the expected clauses
  • Some of it was ensuring I removed, added and updated the right stringified coordinates in my tile subsets as the ball and paddle moved

Eventually, I ran my program and started seeing game boards instead of errors!

That made me confident enough to temporarily comment out the console.log() statements and function calls that rendered the new state of the game board.

...and run the program again...

OMG! It worked!

  • My program returned a single value: a 5-digit number
  • It was the correct answer!

Building a Block Breaker game simulator

  • I knew I had to build a simulator to see the game play out
  • It required some gross code, but that's ok

I also had to get creative with my core loops:

  • The first 3,000+ output values had to pass just to render the initial state of the game board
  • I didn't want a visitor to wait for that code to run
  • After that, there's no telling how long it takes for the next three output values to generate
  • Since I had an interval play out every output value at the same speed, initial runs of the simulator made the game feel super sluggish

To speed things up:

  • I run the program instantly until the first score is generated
  • Then I start the interval, running it every 1ms
  • In each iteration of the interval, I instantly skip to the moment that three new output values are generated

The result is a serious 'speed'-run!

And it's one of the most rewarding - perhaps coolest - simulators I built thus far.

Try the simulator using your puzzle input!
Simulator of Block Breaker game

I did it!!!

  • I solved both parts!
  • I built a simulator that lets you watch the game being played!
  • I've now solved 6/6 Intcode puzzles!
  • And I've built 6 simulators, one for each iteration of my Intcode computer!

My head hurts from scratching it so hard while solving Part 2.

Thankfully, my grit paid off!

Oldest comments (0)