diff --git a/2015/lib/2015/7.ex b/2015/lib/2015/7.ex
index a166bc7..6598a08 100644
--- a/2015/lib/2015/7.ex
+++ b/2015/lib/2015/7.ex
@@ -2,13 +2,157 @@ import AOC
aoc 2015, 7 do
import NimbleParsec
+ use Bitwise
- def parse_inst(line) do
+ defparsec(
+ :wire_label,
+ ascii_string([?a..?z], min: 1)
+ |> unwrap_and_tag(:wire)
+ )
+
+ defparsec(
+ :connects_to,
+ ignore(string(" -> "))
+ )
+
+ defparsec(
+ :signal,
+ integer(min: 1)
+ |> unwrap_and_tag(:signal)
+ )
+
+ defparsec(
+ :input,
+ choice([
+ parsec(:signal),
+ parsec(:wire_label)
+ ])
+ )
+
+ defparsec(
+ :and,
+ parsec(:input)
+ |> ignore(string(" AND "))
+ |> parsec(:input)
+ |> tag(:and)
+ )
+
+ defparsec(
+ :or,
+ parsec(:input)
+ |> ignore(string(" OR "))
+ |> parsec(:input)
+ |> tag(:or)
+ )
+
+ defparsec(
+ :lshift,
+ parsec(:wire_label)
+ |> ignore(string(" LSHIFT "))
+ |> integer(min: 1)
+ |> tag(:lshift)
+ )
+
+ defparsec(
+ :rshift,
+ parsec(:wire_label)
+ |> ignore(string(" RSHIFT "))
+ |> integer(min: 1)
+ |> tag(:rshift)
+ )
+
+ defparsec(
+ :not,
+ ignore(string("NOT "))
+ |> parsec(:wire_label)
+ |> tag(:not)
+ )
+
+ defparsec(
+ :instruction_parsec,
+ choice([
+ parsec(:and),
+ parsec(:or),
+ parsec(:lshift),
+ parsec(:rshift),
+ parsec(:not),
+ parsec(:input)
+ ])
+ |> parsec(:connects_to)
+ |> parsec(:wire_label)
+ )
+
+ def unwrap_parsec_result({:ok, [{tag, attrs}, {:wire, assign}], _, _, _, _}) do
+ attrs = List.wrap(attrs)
+ {assign, List.to_tuple([tag | attrs])}
end
+ def parse_instruction(str),
+ do:
+ str
+ |> instruction_parsec()
+ |> unwrap_parsec_result()
+
+ def circuit(), do: input_stream() |> Stream.map(&parse_instruction/1) |> Enum.into(%{})
+
def p1 do
+ :ets.new(:memo, [:set, :named_table])
+
+ circuit()
+ |> trace("a")
end
def p2 do
+ :ets.new(:memo, [:set, :named_table])
+
+ circuit()
+ |> Map.put("b", 956)
+ |> trace("a")
end
+
+ def trace(circuit, wire) when is_binary(wire) do
+ case :ets.lookup(:memo, wire) do
+ [{^wire, value}] ->
+ value
+
+ _ ->
+ value = trace(circuit, Map.get(circuit, wire))
+ :ets.insert(:memo, {wire, value})
+ value
+ end
+ end
+
+ def trace(_circuit, signal) when is_integer(signal) do
+ signal
+ end
+
+ def trace(circuit, {:wire, wire}) do
+ trace(circuit, wire)
+ end
+
+ def trace(circuit, {:signal, signal}) do
+ trace(circuit, signal)
+ end
+
+ def trace(circuit, {:or, lhs, rhs}) do
+ trace(circuit, lhs) ||| trace(circuit, rhs)
+ end
+
+ def trace(circuit, {:and, lhs, rhs}) do
+ trace(circuit, lhs) &&& trace(circuit, rhs)
+ end
+
+ def trace(circuit, {:lshift, lhs, rhs}) do
+ trace(circuit, lhs) <<< trace(circuit, rhs)
+ end
+
+ def trace(circuit, {:rshift, lhs, rhs}) do
+ trace(circuit, lhs) >>> trace(circuit, rhs)
+ end
+
+ def trace(circuit, {:not, arg}) do
+ ~~~trace(circuit, arg)
+ end
+
+ def trace(_circuit, other), do: other
end