Jesper Dramsch , , read in 4 minutes

We're parsing instructions and plotting in the terminal today!

What is Advent of Code?

In this little series, I want to write about the thought processes I have behind solving the puzzles presented in Advent of Code.

During the Advent of Code small coding puzzles are presented with varying levels of difficulty. They're always Christmas-themed text puzzles where you help elves out of some mess to save the holidays. The puzzles are deliberately ambiguous to not bias puzzlers into a certain solution, they always have an example input in the text to explain some details and an input file for each day that comes in the form of a text file you can download. The solution is a simple number or a string you enter, which is checked by the website automatically. After you finish part 1, part 2 unlocks, which is a variation on part 2. This can be a little twist on part 1 or a large twist that changes the problem formulation into a problem that would take brute force solutions to compute longer than the heat death of the universe would take.

Find my solutions here:

These descriptions won't be a perfect narrative, but rather how I think about these problems and where I see different solutions. I apologise for the possibility of rambling!

The example input

Is this this year's compiler problem? Noice!

addx 3
addx -5

Right away I can say that I should use match-case statements

The implementation

This is another brain-in-a-knot problem.

We have instructions that take different amounts of time to complete. We want to have the values at certain times that may be in between instructions.

So I add an increment inc in addition to the add argument.

def process_instruction(instruction, cycles, X):
    add = 0
    match instruction.split(" "):
        case "noop",:
            inc = 1
        case "addx", add:
            inc = 2
            add = int(add)
    return inc, add

Important to note that we have X that starts at 1 too.

I wonder if some sort of queue system will be necessary and I'm terrified of what part 2 might throw at us. 👀

I think I want to go "sparse" with this one. I could set up a queue I fill up, but I can also simply increase the counters with a check, so I don't run into troubles, when the queues suddenly get long.

cycles, X = 0, 1
for line in instructions:
    inc, add = process_instruction(line, cycles, X)
    if cycles < check <= cycles + inc:
        checks[check] = X
        check += 40
    cycles += inc
    X += add

Fairly simple.

Part Deux

Part 2 needed a bit of refactoring so I can reuse the instruction processing you saw in part 1.

Everything else was just using the inc and add arguments in different ways:

inc, add = process_instruction(line, cycles, X)
for i in range(inc):
    if X - 1 <= cycles % 40 <= X + 1:

Then I simply added a little function to draw the CRT

def plot_crt(crt):
    return "\n".join("".join(crt[i : i + 40]) for i in range(0, len(crt), 40))

And it was the first solution I couldn't submit automagically, but I love these honestly.

Error Log

Annoyingly repeated a mistake by forgetting to set a variable this time around and hade a small misunderstanding of part 2 which was quickly resolved.

  • cycle += 1 UnboundLocalError: cannot access local variable 'cycle' where it is not associated with a value forgot to start cycles
  • check = checks.values()[0] TypeError: 'dict_values' object is not subscriptable thought that would be a list or something.
  • check = checks[20] KeyError: 20 oh my. Good thing I caught that, I set up my range() wrong.
  • if cycles < check <= cycles+inc: TypeError: '<' not supported between instances of 'int' and 'NoneType' Time to debug what I messed up here! I clearly don't understand the match statement well enough yet. Ok, since I split on an empty space the "noop" line still is a list, so the case has to also look into a list like this: case "noop",:. Good to know.
  • if cycles < check <= cycles+inc: TypeError: '<' not supported between instances of 'int' and 'NoneType' ah. Messed up my logic about using checks. It should be the keys I'm working with and I also caught that I need to increment it.
  • return sum(cyclce*X for cycle, X in checks.items()) NameError: name 'cyclce' is not defined. Did you mean: 'cycle'? haha, those typos will get me every time.
  • return inc, add UnboundLocalError: cannot access local variable 'add' where it is not associated with a value you'd think I'd learn this by now.
  • return plot_crt(draw_crt(instructions)) NameError: name 'instructions' is not defined argh. wrong name!
  • Bit of an understanding error about the CRT. I thought it was continuous indexing, but in fact it is for every line individually. Easy fix!