Advent of Code 2016 - Day 12: Leonardo's Monorail

Adam Boucek profile

Adam Boucek

|

June 26th 2023

KotlinAOC

Overview of the problem

My last AOC task is day 12 - 2016. I want to read the instruction from an assembunny code and get value input of a. We have four registers a, b, c, and d. All registers start at 0, and can hold only integers.

The types of instruction we can get are only these:

  • cpy x y copies x (either an integer or the value of a register) into register y.
  • inc x increases the value of register x by one.
  • dec x decreases the value of register x by one.
  • jnz x y jumps to an instruction y away (positive means forward; negative means backward), but only if x is not zero.

The set of instructions might look like this:

cpy 1 a
cpy 1 b
cpy 26 d
cpy 7 c
inc d
dec c
jnz c -2
cpy a c

The instructions tell us to shift the numbers throughout the register.

Solution

The solution for the first and second part is almost identical, so I combined them into one section.

Firstly, I created the register with the four letters that hold the integer 0 at the start. I used a map with keys of strings and values of integers. I decided to use String over Char because I wanted to avoid having a problem with the file input that returns a string.

Also, I have to keep track of steps (iterations), since instruction jnz can jump away.

val register = mutableMapOf("a" to 0, "b" to 0, "c" to 0, "d" to 0)

Secondly, I iterate through the instruction text file line by line. This time I cannot use .forEach() as used in previous AoC, because of the jump operation, we might (and we will) end up iterating more times than the input size/length.

We will iterate the set of instructions based on the steps. It cannot jump below 0 or greater than the length of the input file. We will use a string split for each iteration to prepare the instructions for further operations.

while (steps <= 0 && steps < input.size) { val words = input[steps].split(" ") ... }

Then I take the first word from the instruction array and decide whether we copy a number, decrease, increase or jump away. The first three instructions are pretty straightforward. cpy copies an integer from the input and pastes it to the register or copies value between registers. Increase and decrease changes value by one. After each operations, we add one to the steps variable. Like so:

when (words.first()) { "cpy" -> { if (words[1] in "a".."d") register[words[2]] = register[words[1]]!!.toInt() else register[words[2]] = words[1].toInt() steps++ } "inc" -> { register[words[1]] = register[words[1]]!! + 1 steps++ } "dec" -> { register[words[1]] = register[words[1]]!! - 1 steps++ } ... }

The last part is a little more complicated. We check if the jump instruction points to a register first. If it does, we check whether the register is not zero. If it is not a zero, we add the number to the number of steps we "skip" or " rollback" in our sets of instructions. If the register holds zero, we add only 1. Lastly, if the first instruction is not a key of a register, we automatically jump by the second number.

"jnz" -> { steps += if (words[1] in "a".."d") { if (register[words[1]]!! > 0) words[2].toInt() else 1 } else words[2].toInt() }

The second part was almost identical to the first part. We only start with register c to be 0.

Reflection

The project took me two afternoons (~6 hours). I struggled with the jump operation the most. Now looking at it, it looks really simple and straightforward, but it starched my brain.

I am glad I learned how to implement if condition with range of strings by if (letter in "a".."d"). It was very helpful. The next thing I earned was implementing a map. Normally, if I used JavaScript, I would create an object with these key values. However, this collection type was even easier to operate with than I expected.

Advent of Code 2016 - Day 12: Leonardo's Monorail

GitHub Repo