defmodule AOC.Day20 do # use AOC.Day, day: 20, input: "example" use AOC.Day, day: 20 def parse_input(lines) do modules = lines |> Enum.map(fn line -> [name, destination_modules] = String.split(line, " -> ") destination_modules = String.split(destination_modules, ", ") case String.at(line, 0) do "%" -> {String.trim_leading(name, "%"), {:flip_flop, destination_modules, :off}} "&" -> {String.trim_leading(name, "&"), {:conjunction, destination_modules, %{}}} _ -> {name, {:broadcast, destination_modules}} end end) |> Enum.into(%{}) Enum.reduce(modules, modules, fn {module_name, module}, acc -> module |> elem(1) |> Enum.reduce(acc, fn destination_name, acc -> case Map.fetch(acc, destination_name) do {:ok, {:conjunction, destination_names, states} = destination_module} -> Map.put( acc, destination_name, {:conjunction, destination_names, Map.put(states, module_name, :low)} ) _ -> acc end end) end) end def press_button(modules) do pulse_queue = :queue.new() pulse_queue = :queue.in({:low, "broadcaster", "button"}, pulse_queue) propagate_pulses(modules, pulse_queue, {0, 0}) end def send_pulses(pulse_queue, pulse_strength, destination_names, from) do Enum.reduce(destination_names, pulse_queue, fn destination_name, acc -> :queue.in({pulse_strength, destination_name, from}, acc) end) end def propagate_pulses(modules, pulse_queue, {low_count, high_count}) do case :queue.out(pulse_queue) do {:empty, _} -> {modules, {low_count, high_count}} {{:value, {pulse_strength, module_name, from}}, pulse_queue} -> {modules, pulse_queue} = case Map.fetch(modules, module_name) do :error -> {modules, pulse_queue} {:ok, {:broadcast, destination_names}} -> pulse_queue = send_pulses(pulse_queue, pulse_strength, destination_names, module_name) {modules, pulse_queue} {:ok, {:flip_flop, destination_names, state}} -> case pulse_strength do :high -> {modules, pulse_queue} :low -> case state do :off -> module = {:flip_flop, destination_names, :on} pulse_queue = send_pulses(pulse_queue, :high, destination_names, module_name) {Map.put(modules, module_name, module), pulse_queue} :on -> module = {:flip_flop, destination_names, :off} pulse_queue = send_pulses(pulse_queue, :low, destination_names, module_name) {Map.put(modules, module_name, module), pulse_queue} end end {:ok, {:conjunction, destination_names, state}} -> state = Map.put(state, from, pulse_strength) modules = Map.put(modules, module_name, {:conjunction, destination_names, state}) all_high? = state |> Map.values() |> Enum.all?(&(&1 == :high)) pulse_queue = if all_high? do send_pulses(pulse_queue, :low, destination_names, module_name) else send_pulses(pulse_queue, :high, destination_names, module_name) end {modules, pulse_queue} end low_count = low_count + if pulse_strength == :low, do: 1, else: 0 high_count = high_count + if pulse_strength == :high, do: 1, else: 0 propagate_pulses(modules, pulse_queue, {low_count, high_count}) end end def part1(modules) do {_, {lows, highs}} = Enum.reduce(1..1000, {modules, {0, 0}}, fn _, {modules, {low_acc, high_acc}} -> {modules, {low, high}} = press_button(modules) {modules, {low_acc + low, high_acc + high}} end) lows * highs end def part2(_input) do "TODO" end end