import AOC aoc 2015, 7 do import NimbleParsec use Bitwise 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 circuit() |> trace("a") |> then(&elem(&1, 1)) end def p2 do a = p1() circuit() |> Map.put("b", a) |> trace("a") |> then(&elem(&1, 1)) end def trace(circuit, wire) when is_binary(wire) do {circuit, traced} = trace(circuit, Map.get(circuit, wire)) {Map.put(circuit, wire, traced), traced} end def trace(circuit, signal) when is_integer(signal) do {circuit, 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 {circuit, lhs} = trace(circuit, lhs) {circuit, rhs} = trace(circuit, rhs) {circuit, lhs ||| rhs} end def trace(circuit, {:and, lhs, rhs}) do {circuit, lhs} = trace(circuit, lhs) {circuit, rhs} = trace(circuit, rhs) {circuit, lhs &&& rhs} end def trace(circuit, {:lshift, lhs, rhs}) do {circuit, lhs} = trace(circuit, lhs) {circuit, rhs} = trace(circuit, rhs) {circuit, lhs <<< rhs} end def trace(circuit, {:rshift, lhs, rhs}) do {circuit, lhs} = trace(circuit, lhs) {circuit, rhs} = trace(circuit, rhs) {circuit, lhs >>> rhs} end def trace(circuit, {:not, arg}) do {circuit, arg} = trace(circuit, arg) {circuit, ~~~arg} end end