Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

aoc 2019 day 5

2019/elixir/day-5.livemd

aoc 2019 day 5

Setup

Mix.install([{:kino, "~> 0.5.1"}])
input = Kino.Input.textarea("")
init_state =
  input
  |> Kino.Input.read()
  |> String.split(",", trim: true)
  |> Enum.map(&String.to_integer/1)
  |> Enum.with_index()
  |> Map.new(fn {v, k} -> {k, v} end)

Part 1

defmodule Part1.Computer do
  def compute(init_state, max_step, input) do
    0..max_step
    |> Enum.reduce_while({init_state, []}, fn
      _step, {0, {state, outputs}} ->
        {:cont, {state, outputs}}

      _step, {skip, {state, outputs}} when is_integer(skip) ->
        {:cont, {skip - 1, {state, outputs}}}

      step, {state, outputs} ->
        inst = state[step]
        op = rem(inst, 100)
        params = get_parameters(inst, op)

        case op do
          99 ->
            {:halt, {state, outputs}}

          1 ->
            [p1, p2] = params
            res = get_value(p1, state[step + 1], state) + get_value(p2, state[step + 2], state)
            new_state = put_value(state, state[step + 3], res)
            {:cont, {2, {new_state, outputs}}}

          2 ->
            [p1, p2] = params
            res = get_value(p1, state[step + 1], state) * get_value(p2, state[step + 2], state)
            new_state = put_value(state, state[step + 3], res)
            {:cont, {2, {new_state, outputs}}}

          3 ->
            # IO.puts("value: #{state[step + 1]}")
            new_state = put_value(state, state[step + 1], input)
            {:cont, {0, {new_state, outputs}}}

          4 ->
            [param] = get_parameters(inst, op)
            output = get_value(param, state[step + 1], state)

            # IO.puts("output inst: #{inst} param: #{param}. pos: #{state[step + 1]} value: #{output}")
            {:cont, {0, {state, [output | outputs]}}}
        end
    end)
  end

  defp get_parameters(instruction, op_code) when op_code in [1, 2, 5, 6, 7, 8] do
    [
      instruction |> div(100) |> rem(10),
      instruction |> div(1_000) |> rem(10)
    ]
  end

  defp get_parameters(instruction, op_code) when op_code in [3, 4] do
    [instruction |> div(100) |> rem(10)]
  end

  defp get_parameters(_, _), do: []

  defp get_value(1, value, _state), do: value
  defp get_value(0, position, state), do: Map.get(state, position)

  defp put_value(state, position, value), do: Map.put(state, position, value)
end

max_step = init_state |> Map.keys() |> Enum.max()

Part1.Computer.compute(init_state, max_step, 1)

Part 2

defmodule Part2.Computer do
  def compute(pointer, state, input, outputs \\ []) do
    inst = state[pointer]
    op = rem(inst, 100)
    params = get_parameters(inst, op)
    move_operation(op, pointer, params, state, input, outputs)
  end

  defp move_operation(op_code, pointer, [p1, p2] = _params, state, input, outputs)
       when op_code in [1, 2] do
    v1 = get_value(p1, state, pointer + 1)
    v2 = get_value(p2, state, pointer + 2)
    res = calc(op_code).(v1, v2)
    new_state = put_value(state, pointer + 3, res)
    next_pointer = pointer + 4
    compute(next_pointer, new_state, input, outputs)
  end

  defp move_operation(3, pointer, [p1, _p2], state, input, outputs) do
    new_state = put_value(state, pointer + 1, input)
    next_pointer = pointer + 2
    compute(next_pointer, new_state, input, outputs)
  end

  defp move_operation(4, pointer, [p1, _p2], state, input, outputs) do
    next_pointer = pointer + 2
    output = get_value(p1, state, pointer + 1)
    compute(next_pointer, state, input, [output | outputs])
  end

  defp move_operation(5, pointer, [p1, p2], state, input, outputs) do
    param = get_value(p1, state, pointer + 1)
    next_pointer = if param != 0, do: get_value(p2, state, pointer + 2), else: pointer + 3
    compute(next_pointer, state, input, outputs)
  end

  defp move_operation(6, pointer, [p1, p2], state, input, outputs) do
    param = get_value(p1, state, pointer + 1)
    next_pointer = if param == 0, do: get_value(p2, state, pointer + 2), else: pointer + 3
    compute(next_pointer, state, input, outputs)
  end

  defp move_operation(7, pointer, [p1, p2], state, input, outputs) do
    v1 = get_value(p1, state, pointer + 1)
    v2 = get_value(p2, state, pointer + 2)
    value = if v1 < v2, do: 1, else: 0
    new_state = put_value(state, pointer + 3, value)
    next_pointer = pointer + 4
    compute(next_pointer, new_state, input, outputs)
  end

  defp move_operation(8, pointer, [p1, p2], state, input, outputs) do
    v1 = get_value(p1, state, pointer + 1)
    v2 = get_value(p2, state, pointer + 2)
    value = if v1 == v2, do: 1, else: 0
    new_state = put_value(state, pointer + 3, value)
    next_pointer = pointer + 4
    compute(next_pointer, new_state, input, outputs)
  end

  defp move_operation(99, _pointer, _, state, _, outputs), do: {state, outputs}

  defp calc(1), do: &amp;Kernel.+/2
  defp calc(2), do: &amp;Kernel.*/2

  defp get_parameters(instruction, op_code) do
    [
      instruction |> div(100) |> rem(10),
      instruction |> div(1_000) |> rem(10)
    ]
  end

  defp get_value(1, state, position), do: state[position]
  defp get_value(0, state, position), do: state[state[position]]

  defp put_value(state, position, value), do: Map.put(state, state[position], value)
end

Part2.Computer.compute(0, init_state, 5)
|> elem(1)