Powered by AppSignal & Oban Pro

Day 14

day_14.livemd

Day 14

Setup

Mix.install([
  {:kino, "~> 0.4.1"}
])
input = Kino.Input.textarea("Please provide your input: ")
[template, rules] =
  input
  |> Kino.Input.read()
  |> String.split("\n\n")

rules =
  rules
  |> String.split("\n")
  |> Enum.map(fn rule ->
    [pair, element] = String.split(rule, " -> ")
    {pair, element}
  end)
  |> Enum.reduce(%{}, fn {key, value}, acc ->
    Map.put(acc, key, value)
  end)

Part 1

defmodule AOC.DayFourteen do
  def solve(template, rules) do
    {min, max} =
      0..9
      |> Enum.reduce(template, fn _step, template ->
        polymerize(template, rules)
      end)
      |> String.graphemes()
      |> Enum.frequencies()
      |> Enum.map(fn {_key, value} ->
        value
      end)
      |> Enum.min_max()

    max - min
  end

  def polymerize(template, rules) do
    polymerize(String.graphemes(template), rules, [])
  end

  def polymerize([first, second], rules, new) do
    key = first <> second
    to_insert = first <> rules[key] <> second

    [to_insert | new]
    |> Enum.reverse()
    |> Enum.join()
  end

  def polymerize([first | [second | _rest] = rest], rules, new) do
    key = first <> second
    to_insert = first <> rules[key]
    polymerize(rest, rules, [to_insert | new])
  end
end

AOC.DayFourteen.solve(template, rules)

Part 2

defmodule AOC.DayFourteen.Part2 do
  def solve(template, rules) do
    parsed = template |> parse()

    initial_count = String.graphemes(template) |> Enum.frequencies()

    {_polymer, count} =
      1..40
      |> Enum.reduce({parsed, initial_count}, fn _step, {template, count} ->
        polymerize(template, rules, count)
      end)

    {min, max} = count |> Enum.map(fn {_k, v} -> v end) |> Enum.min_max()
    max - min
  end

  def parse(template) do
    template
    |> String.graphemes()
    |> parse([])
  end

  def parse([first, second], acc) do
    [{first <> second, {first, second}} | acc]
    |> Enum.frequencies()
  end

  def parse([first | [second | _rest] = rest], acc) do
    parse(rest, [{first <> second, {first, second}} | acc])
  end

  def polymerize(template, rules, count) do
    Enum.reduce(template, {%{}, count}, fn {{key, {first, second}}, amount}, {template, count} ->
      element = rules[key]

      first_new = {first <> element, {first, element}}
      second_new = {element <> second, {element, second}}

      template =
        template
        |> put_or_update(first_new, amount)
        |> put_or_update(second_new, amount)

      count = put_or_update(count, element, amount)

      {template, count}
    end)
  end

  def put_or_update(map, key, value) do
    case map[key] do
      nil -> Map.put(map, key, value)
      _x -> Map.update!(map, key, &(&1 + value))
    end
  end
end

AOC.DayFourteen.Part2.solve(template, rules)