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

Day 14

day14.livemd

Day 14

Helpers

defmodule Helpers do
  def data() do
    "data/day14.txt"
    |> File.stream!()
    |> Stream.map(&String.trim/1)
    |> parse()
  end

  def parse(data) do
    template = data |> Enum.at(0) |> build_template()
    insertions = data |> Stream.drop(2) |> build_insertions()
    {template, insertions}
  end

  def build_template(str) do
    str
    |> String.graphemes()
    |> Enum.map(&String.to_atom/1)
    |> Enum.chunk_every(2, 1, [nil])
    |> Enum.map(&List.to_tuple/1)
    |> Enum.frequencies()
  end

  def build_insertions(insertions_list) do
    insertions_list
    |> Enum.map(&build_insertion/1)
    |> Enum.into(%{})
  end

  @pair_re ~r{^(\w)(\w) -> (\w)$}
  def build_insertion(pair_str) do
    [^pair_str, l, r, i] = Regex.run(@pair_re, pair_str)
    l = String.to_atom(l)
    r = String.to_atom(r)
    i = String.to_atom(i)
    {{l, r}, [{l, i}, {i, r}]}
  end

  def insert(template, insertions) do
    template
    |> Enum.reduce(%{}, fn {pair, count}, acc ->
      insertions
      |> Map.get(pair, [pair])
      |> Enum.reduce(acc, fn inserted_pair, acc2 ->
        acc2 |> Map.update(inserted_pair, count, &(&1 + count))
      end)
    end)
  end

  def insert_times(template, insertions, times) do
    template
    |> Stream.iterate(&insert(&1, insertions))
    |> Stream.drop(times)
    |> Enum.at(0)
  end

  def counts(template) do
    template
    |> Enum.reduce(%{}, fn {{l, _r}, count}, acc ->
      Map.update(acc, l, count, &(&1 + count))
    end)
  end

  def min_max(template) do
    template
    |> counts()
    |> Enum.min_max_by(fn {_k, v} -> v end)
    |> then(fn {{_, min_count}, {_, max_count}} -> {min_count, max_count} end)
  end
end

Part 1

import Helpers

{template, insertions} = data()

template
|> insert_times(insertions, 10)
|> min_max()
|> then(fn {min_count, max_count} -> max_count - min_count end)

Part 2

import Helpers

{template, insertions} = data()

template
|> insert_times(insertions, 40)
|> min_max()
|> then(fn {min_count, max_count} -> max_count - min_count end)