Advent of Code 2020 in Elixir - Day 14

Docking Data

defmodule Aoc2020.Day14 do
  @moduledoc "Docking Data"

  def run() do
    parse_input()
    |> run_program()
    |> (fn {memory, _} -> Map.values(memory) end).()
    |> Enum.filter(&(&1 != 0))
    |> Enum.sum()
  end

  def parse_input() do
    File.read!("priv/inputs/2020/day14.txt")
    |> String.trim()
    |> String.split("\n")
    |> Enum.map(&String.split(&1, [" = ", "[", "]"]))
    |> Enum.map(&parse_instruction/1)
  end

  def parse_instruction(["mask", mask]) do
    {:update_mask, mask}
  end

  def parse_instruction(["mem", location, _, value]) do
    {:update_memory, {String.to_integer(location), value}}
  end

  def run_program(instructions) do
    Enum.reduce(
      instructions,
      {%{}, ""},
      fn instruction, {memory, mask} ->
        handle_instruction(instruction, memory, mask)
      end
    )
  end

  def handle_instruction({:update_mask, mask}, memory, _) do
    {memory, :binary.bin_to_list(mask)}
  end

  def handle_instruction({:update_memory, {location, value}}, memory, mask) do
    new_value = apply_mask(mask, value)
    {Map.put(memory, location, new_value), mask}
  end

  def apply_mask(mask, value) do
    input =
      String.to_integer(value)
      |> Integer.to_string(2)
      |> String.pad_leading(36, "0")
      |> :binary.bin_to_list()

    Enum.zip(input, mask)
    |> Enum.map(fn
      {v, ?X} ->
        v

      {_, m} ->
        m
    end)
    |> List.to_integer(2)
  end
end