Robert Mion

Posted on

# Leonardo's Monorail

## Part 1

1. Thank goodness: no reliance on Day 11
2. Assembly code: Round...?
3. Writing my working algorithm

### Thank goodness: no reliance on Day 11

• I failed to solve any parts of Day 11
• That worried me coming in to Day 12

Thankfully, Day 12 is yet another...

### Assembly code: Round...?

• I'm probably remembering incorrectly, but it seems like every year - except 2021 - featured at least one assembly-code-themed puzzle

This feels like an easier one:

• store and update numbers in registers
• jump conditionally
• end when a pointer is out of bounds
• return the value stored in a particular register

I'm off to write the same algorithm for what feels like the third or fourth time...

### Writing my working algorithm

Extracting each line's opcodes and operands:

``````Split the input at each newline character into an array of strings

For each string
Split the string at each space character into an array of two or three strings, saved as op, x and y
If y contains a value (meaning there were three strings)
Construct a three-element array containing the opcode string, and either a string or number for x and y
Else, if y is empty (meaning there were two strings)
Construct a two-element array containing the opcode string, and either a string or number for x
``````

This is the JavaScript I wrote:

``````input.split('\n').map(line => {
let [op, x, y] = line.split(' ')
if (y) {
return [op, isNaN(x) ? x : +x, isNaN(y) ? y : +y]
} else {
return [op, isNaN(x) ? x : +x]
}
})
``````

More setup:

``````Set pointer to 0 so it starts reading at the first instruction
Set registers as an object with four keys, a-d, each with 0 as a value
``````

The dictionary of opcode functions:

``````cpy(x,y) {
registers[y] = (typeof x == 'string') ? registers[x] : x;
pointer++
},
inc(x) {
registers[x]++
pointer++
},
dec(x) {
registers[x]--
pointer++
},
jnz(x,y) {
registers[x] !== 0 ? pointer += y : pointer++
}
``````

The main loop:

``````Do as long as pointer contains a number that could refer to an index within the bounds of the list of instructions
Invoke the appropriate function in the dictionary of opcodes, passing as arguments the second value in the instruction and the third...even if there is no third value
``````
``````while (pointer >= 0 && pointer < rules.length) {
opcodes[rules[pointer][0]](
rules[pointer][1], rules[pointer][2]
)
}
``````

Two mistakes I made while writing the code above:

• I forgot to increment `pointer` in the three non-`jnz` functions
• I wrote `rules[pointer[1]]` instead of `rules[pointer][1]`, confusing the program into expecting some property `1` to exist on the number stored in `pointer`

After correcting those errors, the program ran and generated the correct answer as expected!

## Part 2

1. Let me guess...a performance test?
2. Thankfully, it finishes in a few seconds

### Let me guess...a performance test?

• It seems like a simple task: change `c` to start as `1`, then re-run
• In prior puzzles, changing a register's starting value caused the program to run exponentially longer...seemingly forever!
• Will that be the case here?

### Thankfully, it finishes in a few seconds

• Seven seconds, to be exact - not great
• register `a` contains a number over 9 million - in Part 1 it ended containing a number just over 300 thousand
• I don't care enough to inspect what causes the long runtime - I tried doing that in prior puzzles and it never helped me solve the puzzle
• I'm just glad it finished in under a minute and generated the correct answer!

## I did it!!

• I solved both parts!
• I sadly stumbled over the same mistakes I made in previous attempts: array look-up and pointer incrementing!
• I feel prepared to attempt Part 1 of Day 23!

After solving today's puzzle and returning to the main map, I was delighted to see this animation playing: