diff --git a/23/elixir/inputs/day20.txt b/23/elixir/inputs/day20.txt new file mode 100644 index 0000000..98713c2 --- /dev/null +++ b/23/elixir/inputs/day20.txt @@ -0,0 +1,58 @@ +%cv -> xz +%kt -> qx, rz +%cb -> kt +%pl -> sf, db +%zd -> ln, gf +%bf -> qx, pf +%xz -> jd +%xm -> db +%vz -> cr, vc +%qq -> qm, gf +&xn -> th +%nn -> ff, db +%gx -> cd +&qn -> th +%qk -> vc +&xf -> th +%qj -> xm, db +%fn -> pr, gf +%sf -> bp +%jd -> qx, vm +%mc -> ds, db +%tj -> lc, gf +%jz -> qj, db +%sb -> ks, vc +%ln -> gf, qq +%bx -> qx, qp +broadcaster -> sr, ch, hd, bx +%ch -> db, mc +%ds -> cc +&qx -> cb, cv, bx, xz, vm, zl +%bp -> db, jz +&zl -> th +%vl -> gf, fj +&db -> ff, ds, sf, ch, cc, xf +&th -> rx +%cr -> gx, vc +%sr -> gf, vl +%lr -> sb +%hv -> lr +%cl -> qx, bf +%lc -> gf, fn +%pm -> vc, qk +%cc -> nn +%gm -> tj, gf +%vm -> cl +%ff -> pl +%qp -> cb, qx +%pf -> qx +&vc -> lr, hd, ks, qn, gx, nh, hv +%qm -> gm +%nh -> hv +%rz -> qx, cv +%ks -> vz +%fj -> zd +&gf -> fj, qm, xn, sr +%pr -> gf +%cd -> pm, vc +%hd -> vc, nh diff --git a/23/elixir/inputs/day20_example.txt b/23/elixir/inputs/day20_example.txt new file mode 100644 index 0000000..2dc1bab --- /dev/null +++ b/23/elixir/inputs/day20_example.txt @@ -0,0 +1,5 @@ +broadcaster -> a, b, c +%a -> b +%b -> c +%c -> inv +&inv -> a diff --git a/23/elixir/lib/days/day20.ex b/23/elixir/lib/days/day20.ex new file mode 100644 index 0000000..98a8841 --- /dev/null +++ b/23/elixir/lib/days/day20.ex @@ -0,0 +1,127 @@ +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