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

Day08

day08.livemd

Day08

Setup

Mix.install([
  {:kino, "~> 0.5.0"}
])
sample_text = Kino.Input.textarea("sample")
input_text = Kino.Input.textarea("input")
sample = Kino.Input.read(sample_text)
input = Kino.Input.read(input_text)
defmodule Shared do
  def parse(text) do
    text
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      line
      |> String.split(" ", trim: true)
      |> then(&{hd(&1), hd(tl(&1)) |> String.to_integer()})
    end)
  end

  def debug(instructions), do: debug(instructions, 0, 0, [])

  defp debug(instructions, acc, loc, seen_instructions) do
    if loc == Enum.count(instructions) do
      {:ok, acc}
    else
      {op, param} = Enum.at(instructions, loc)
      {new_acc, new_loc} = execute(op, param, acc, loc)

      if new_loc in seen_instructions do
        {:error, new_acc}
      else
        debug(instructions, new_acc, new_loc, [new_loc | seen_instructions])
      end
    end
  end

  def fix(instructions), do: fix(instructions, 0)

  def fix(instructions, i) do
    case try_to_fix(instructions, i) |> debug do
      {:error, _} -> fix(instructions, i + 1)
      {:ok, acc} -> acc
    end
  end

  def try_to_fix(instructions, i) do
    {op, param} = Enum.at(instructions, i)

    new_op =
      case op do
        "nop" -> "jmp"
        "jmp" -> "nop"
        "acc" -> "acc"
      end

    List.replace_at(instructions, i, {new_op, param})
  end

  defp execute("nop", _param, acc, loc), do: {acc, loc + 1}
  defp execute("acc", param, acc, loc), do: {acc + param, loc + 1}
  defp execute("jmp", param, acc, loc), do: {acc, loc + param}
end

part a

input
|> Shared.parse()
|> Shared.debug()
|> elem(1)

part b

input
|> Shared.parse()
|> Shared.fix()