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

Advent of Code 2015 Day 7 Part 2

2015_day7_part2.livemd

Advent of Code 2015 Day 7 Part 2

Mix.install([
  {:kino_aoc, "~> 0.1"}
])

Get Inputs

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2015", "7", System.fetch_env!("LB_SESSION"))

My answer

import Bitwise
defmodule Solver do
  import Bitwise

  defp bit_not(input) do
    <> = <<(~~~input)::size(16)>>
    output
  end

  defp parse_variable(nil, _), do: nil
  defp parse_variable("", _), do: nil
  defp parse_variable(variable, _) when is_integer(variable), do: variable
  defp parse_variable(variable, dict) do
    case Integer.parse(variable) do
      {number, ""} -> number
      _ -> Map.get(dict, variable, variable)
    end
  end

  defp calc("", input_a, _) when is_integer(input_a), do: input_a
  defp calc("NOT", _, input_b) when is_integer(input_b), do: bit_not(input_b)
  defp calc("AND", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
    input_a &amp;&amp;&amp; input_b
  end
  defp calc("OR", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
    input_a ||| input_b
  end
  defp calc("LSHIFT", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
    input_a <<< input_b
  end
  defp calc("RSHIFT", input_a, input_b) when (is_integer(input_a) and is_integer(input_b)) do
    input_a >>> input_b
  end
  defp calc(_, _, _), do: nil

  defp solve(instruction, dict) do
    %{
      "input_a" => input_a,
      "input_b" => input_b,
      "op" => op,
      "output" => output
    } = instruction

    input_a = parse_variable(input_a, dict)
    input_b = parse_variable(input_b, dict)

    result = calc(op, input_a, input_b)
    solved = !is_nil(result)

    instruction = Map.put(instruction, :solved, solved)
    dict = if solved, do: Map.put(dict, output, result), else: dict

    {instruction, dict}
  end

  def parse_instraction(row) do
    Regex.named_captures(
      ~r/(?[a-z0-9]*) *(?[A-Z]*) *(?[a-z0-9]*) -> (?.+)/,
      row
    )
  end

  def cyclic_solve(%{solved: true}, instructions, dict), do: {instructions, dict}
  def cyclic_solve(new_instruction, instructions, dict) do
    {new_instruction, dict} = solve(new_instruction, dict)

    if new_instruction.solved do
      {instructions, dict} =
        instructions
        |> Enum.reduce({[], dict}, fn sub_instruction, {acc_instructions, acc_dict} ->
          cyclic_solve(sub_instruction, acc_instructions, acc_dict)
        end)

      {[new_instruction | instructions], dict}
    else
      {[new_instruction | instructions], dict}
    end
  end
end
get_a = fn rows ->
  rows
  |> Enum.reduce({[], %{}}, fn row, {instructions, dict} ->
    new_instruction = Solver.parse_instraction(row)
    Solver.cyclic_solve(new_instruction, instructions, dict)
  end)
  |> elem(1)
  |> Map.get("a")
end
rows = String.split(puzzle_input, "\n")

override_value = get_a.(rows)

rows
|> Enum.map(fn row ->
  if String.ends_with?(row, "-> b") do
    "#{override_value} -> b"
  else
    row
  end
end)
|> get_a.()