Andrew Fontaine

Mostly code

Advent of Code 2019: Day 9

10 Dec 2019
Code Snippet for Advent of Code 2019: Day 9

As of today, I’ve beaten my previous year’s record (by one ⭐), but, oh, was it a struggle. The task today is to boost a distress signal to discover what trouble those elves are up to now. First step is to add another parameter mode to the intcode computer! I need to add an offset value to the computer to keep track of the relative mode’s base offset, and add some ways to read the memory utilizing this parameter mode.

  defstruct program: [],
            position: 0,
            input: [],
            output: [],
            offset: 0

  defp parameter(0, x, prog, _computer), do: Enum.at(prog, x, 0)

  defp parameter(1, x, _prog, _computer), do: x

  defp parameter(2, x, prog, %__MODULE__{offset: offset}), do: Enum.at(prog, x + offset, 0)

Notice that I have to pass the computer struct into all of these functions so that I can access the offset in the new mode. I think I could alter this here to drop the prog parameter and pull it from the computer struct as well, but I have some other plans to tune up this computer.

Next, I need to augment writing values to the memory that uses this new mode.

  defp store(prog, pos, v, m, computer) do
    address =
      if m == 2 do
        computer.offset + Enum.at(prog, pos, 0)
      else
        Enum.at(prog, pos, 0)
      end

    prog =
      if length(prog) < address do
        prog ++ List.duplicate(0, address - length(prog))
      else
        prog
      end

    List.update_at(prog, address, fn _ -> v end)
  end

If the length of the program currently is shorter than the address I am trying to write to, I should extend the program’s memory to include that new address. Then, I update the program as normal.

I also need to add a new op-code to handle changing the base offset.

  def op(computer, [_a, _b, c, 0, 9]), do: {:ok, shift_offset(computer, c)}

  defp shift_offset(%__MODULE__{program: prog, position: pos, offset: offset} = computer, m) do
    shift = parameter(m, Enum.at(prog, shift(pos, 1), 0), prog, computer)
    %__MODULE__{computer | offset: shift(offset, shift), position: shift(pos, 2)}
  end

And I should be good to go! Wait…

Day 9 Bad Solution

That’s not good, the opcode 209 is failing 🤔 Maybe I am not adding enough memory when I extend my list? I’ll just add one, off by one errors are classic.

  defp store(prog, pos, v, m, computer) do
    address =
      if m == 2 do
        computer.offset + Enum.at(prog, pos, 0)
      else
        Enum.at(prog, pos, 0)
      end

    prog =
      if length(prog) < address do
        prog ++ List.duplicate(0, address - length(prog) + 1)
      else
        prog
      end

    List.update_at(prog, address, fn _ -> v end)
  end

Run it again!

Day 9 Bad Solution the Second

203!? Well… At least the result is different. Maybe… more memory?

  defp store(prog, pos, v, m, computer) do
    address =
      if m == 2 do
        computer.offset + Enum.at(prog, pos, 0)
      else
        Enum.at(prog, pos, 0)
      end

    prog =
      if length(prog) < address do
        prog ++ List.duplicate(0, address)
      else
        prog
      end

    List.update_at(prog, address, fn _ -> v end)
  end

No more trying to be safe, I’ll just throw as much memory as I can possibly need at this thing. This time I’m serious.

Day 9 Good Solution

Thank goodness it worked!

There’s no puzzle 2 section today, because puzzle 2 was solved by using the newly-constructed intcode computer and no other alterations. It’s not often that the first puzzle is the challenging one. I think I’ll have to use this more going forward, so I plan on cleaning it up. I saw someone suggest a Map instead of a list, so I think I will try that out. This is likely to be a “sparse list”, or a list where a majority of indices in the list are unused, and I can get rid of my funky memory growth code.

Conclusion

Debugging this was quite hard, and I didn’t get a chance to really dive into debugging in Elixir. Fortunately, just throwing memory at the problem fixed it! My next step so to clean this code up, and add some debug level logs to help solve any issues that come up later. I also have a pretty good set of test cases to ensure my refactoring goes smoothly, but it didn’t help me figure out my memory growth issue. It may read short, but it took me pretty much all day to figure that one out 😧

I ❤ feedback. Let me know what you think of this article on Twitter @afontaine_ca