Powered by AppSignal & Oban Pro

Day 7: Some Assembly Required

2015/day07.livemd

Day 7: Some Assembly Required

Mix.install([:kino, :nimble_parsec])

Section

input = Kino.Input.textarea("Input")
defmodule Instructions do
  import NimbleParsec

  signal =
    choice([
      ascii_string([?a..?z], min: 1, max: 2),
      integer(min: 1)
    ])

  binops =
    choice([
      string(" AND ") |> replace(:and),
      string(" OR ") |> replace(:or),
      string(" LSHIFT ") |> replace(:lshift),
      string(" RSHIFT ") |> replace(:rshift)
    ])

  uop =
    string("NOT ")
    |> replace(:not)
    |> concat(signal)

  bop =
    signal
    |> concat(binops)
    |> concat(signal)

  defparsec(:parse, choice([uop, bop, signal]) |> ignore(string(" -> ")) |> concat(signal))
end

schema =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(fn line ->
    {:ok, inst, _, _, _, _} = Parser.instruction(line)
    inst
  end)
  |> Map.new(fn inst ->
    {inst, [output]} = Enum.split(inst, -1)
    {output, inst}
  end)
table = :ets.new(:kb, [:set])
defmodule Emulator do
  import Bitwise

  def eval(_, signal, _) when is_integer(signal), do: signal

  def eval(schema, signal, table) do
    if :ets.member(table, signal) do
      :ets.lookup_element(table, signal, 2)
    else
      case schema[signal] do
        [int] when is_integer(int) ->
          :ets.insert(table, {signal, int})
          int

        [val] ->
          eval(schema, val, table)

        [:not, i] ->
          bxor(eval(schema, i, table), 0xFFFF)
          |> tap(fn int -> :ets.insert(table, {signal, int}) end)

        [i1, :or, i2] ->
          (eval(schema, i1, table) ||| eval(schema, i2, table))
          |> tap(fn int -> :ets.insert(table, {signal, int}) end)

        [i1, :and, i2] ->
          (eval(schema, i1, table) &&& eval(schema, i2, table))
          |> tap(fn int -> :ets.insert(table, {signal, int}) end)

        [i1, :lshift, i2] ->
          eval(schema, i1, table) <<< eval(schema, i2, table)
          |> tap(fn int -> :ets.insert(table, {signal, int}) end)

        [i1, :rshift, i2] ->
          eval(schema, i1, table) >>> eval(schema, i2, table)
          |> tap(fn int -> :ets.insert(table, {signal, int}) end)
      end
    end
  end
end

Emulator.eval(schema, "a", table)
table2 = :ets.new(:part2, [:set])
%{schema | "b" => [Emulator.eval(schema, "a", table)]}
|> Emulator.eval("a", table2)