Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 14

2021/day_14.livemd

Day 14

Input

Mix.install([{:kino, github: "livebook-dev/kino"}])
textarea = Kino.Input.textarea("Input:")

Common

defmodule Common do
  def parse_input(raw_input) do
    [template, insertions] = String.split(raw_input, "\n\n", trim: true)

    counts =
      template
      |> String.split("", trim: true)
      |> Enum.frequencies()

    template =
      template
      |> String.split("", trim: true)
      |> Enum.chunk_every(2, 1, :discard)
      |> Enum.frequencies()
      |> Map.new()

    insertions =
      insertions
      |> String.split("\n", trim: true)
      |> Map.new(fn insertion ->
        [key, value] = String.split(insertion, " -> ", trim: true)
        key = String.split(key, "", trim: true)

        {key, value}
      end)

    {template, insertions, counts}
  end

  def run({template, insertions, counts}, steps) do
    1..steps
    |> Enum.reduce({template, counts}, fn _step, {template, counts} ->
      step(template, insertions, counts)
    end)
    |> then(fn {_template, counts} ->
      {{_, min}, {_, max}} = Enum.min_max_by(counts, &elem(&1, 1))
      max - min
    end)
  end

  def step(template, insertions, counts) do
    Enum.reduce(template, {template, counts}, fn {[a, b] = pair, count}, {template, counts} ->
      to_insert = insertions[pair]
      new_pair_1 = [a, to_insert]
      new_pair_2 = [to_insert, b]

      template =
        template
        |> Map.update(new_pair_1, count, &(&1 + count))
        |> Map.update(new_pair_2, count, &(&1 + count))
        |> Map.update!(pair, &(&1 - count))

      counts = Map.update(counts, to_insert, count, &(&1 + count))

      {template, counts}
    end)
  end
end
raw_input = Kino.Input.read(textarea)
input = Common.parse_input(raw_input)

Part 1

defmodule Part1 do
  def run(input), do: Common.run(input, 10)
end
Part1.run(input)

Part 2

defmodule Part2 do
  def run(input), do: Common.run(input, 40)
end
Part2.run(input)