Powered by AppSignal & Oban Pro

Day 14: Extended Polymerization

backup_2021_original/14.livemd

Day 14: Extended Polymerization

Intro

https://adventofcode.com/2021/day/14

Input

defmodule I do
  def prepare(s) do
    [start, templates] = String.split(s, "\n\n", trim: true)
    start = String.graphemes(start)

    templates =
      String.split(templates, "\n", trim: true)
      |> Enum.map(fn l ->
        [i, r] = String.split(l, " -> ", trim: true)
        [i1, i2] = String.graphemes(i)
        {{i1, i2}, r}
      end)
      |> Map.new()

    #   |> Enum.map(fn l ->
    #       [i, r] = String.split(l, " -> ")
    #       {i,r} end)

    {start, templates}
  end
end

{start_test, templates_test} =
  """
  NNCB

  CH -> B
  HH -> N
  CB -> H
  NH -> C
  HB -> C
  HC -> B
  HN -> C
  NN -> C
  BH -> H
  NC -> B
  NB -> B
  BN -> B
  BB -> N
  BC -> B
  CC -> N
  CN -> C
  """
  |> I.prepare()

{start, templates} =
  File.read!("input14.txt")
  |> I.prepare()
{["O", "N", "S", "V", "V", "H", "N", "C", "F", "V", "B", "H", "K", "V", "P", "C", "H", "C", "P",
  "V"],
 %{
   {"C", "S"} => "C",
   {"P", "O"} => "F",
   {"N", "S"} => "V",
   {"B", "V"} => "K",
   {"C", "V"} => "O",
   {"B", "C"} => "S",
   {"O", "B"} => "O",
   {"H", "S"} => "H",
   {"F", "N"} => "N",
   {"O", "F"} => "K",
   {"H", "H"} => "N",
   {"P", "H"} => "F",
   {"K", "N"} => "O",
   {"B", "O"} => "C",
   {"O", "H"} => "K",
   {"S", "P"} => "C",
   {"B", "B"} => "K",
   {"O", "S"} => "P",
   {"V", "B"} => "V",
   {"V", "P"} => "P",
   {"P", "N"} => "O",
   {"K", "B"} => "F",
   {"V", "O"} => "C",
   {"N", "P"} => "O",
   {"K", "C"} => "S",
   {"B", "F"} => "P",
   {"S", "B"} => "V",
   {"N", "O"} => "H",
   {"F", "H"} => "H",
   {"O", "C"} => "O",
   {"K", "O"} => "V",
   {"C", "N"} => "N",
   {"C", "P"} => "S",
   {"K", "F"} => "B",
   {"S", "O"} => "O",
   {"K", "P"} => "O",
   {"V", "K"} => "F",
   {"H", "N"} => "O",
   {"F", "V"} => "F",
   {"P", "B"} => "F",
   {"F", "C"} => "C",
   {"N", "B"} => "F",
   {"O", "K"} => "O",
   {"F", "O"} => "C",
   {"O", "O"} => "F",
   {"H", "C"} => "S",
   {"F", ...} => "K",
   {...} => "H",
   ...
 }}

Part 1

defmodule P1 do
  def extend(sentence, templ) do
    Enum.chunk_every(sentence, 2, 1)
    |> Enum.map(fn pair ->
      if length(pair) == 2 do
        [p1, p2] = pair
        v = templ[{p1, p2}]
        [p1, v]
      else
        pair
      end
    end)
    |> List.flatten()
  end

  def extend_multiple(sentence, templ, times) do
    Enum.reduce(1..times, sentence, fn i, s ->
      extend(s, templ)
    end)
  end
end
warning: variable "i" is unused (if the variable is not meant to be used, prefix it with an underscore)
  14.livemd#cell:17: P1.extend_multiple/3
{:module, P1, <<70, 79, 82, 49, 0, 0, 9, ...>>, {:extend_multiple, 3}}
items =
  P1.extend_multiple(start_test, templates_test, 10)
  |> Enum.group_by(fn x -> x end)
  |> Enum.map(fn {k, v} -> {k, Enum.count(v)} end)

min = items |> Enum.min_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)
max = items |> Enum.max_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)

max - min == 1588
true
items =
  P1.extend_multiple(start, templates, 10)
  |> Enum.group_by(fn x -> x end)
  |> Enum.map(fn {k, v} -> {k, Enum.count(v)} end)

min = items |> Enum.min_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)
max = items |> Enum.max_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)

max - min
3259

Correct: 3259

Part 2

defmodule P2 do
  def build_elements(templ) do
    Enum.reduce(templ, MapSet.new(), fn {{e1, e2}, e3}, map ->
      map |> MapSet.put(e1) |> MapSet.put(e2) |> MapSet.put(e3)
    end)
    |> MapSet.to_list()
    |> Enum.map(fn e -> {e, 0} end)
    |> Map.new()
  end

  def build_pairs(templ) do
    Enum.map(templ, fn {v1, _v2} -> {v1, 0} end) |> Map.new()
  end

  def build_start(start_d, templates_d) do
    pairs = build_pairs(templates_d)
    elements = build_elements(templates_d)

    elements = Enum.reduce(start_d, elements, fn e, el -> %{el | e => el[e] + 1} end)

    pairs =
      Enum.chunk_every(start_d, 2, 1, :discard)
      |> Enum.reduce(pairs, fn [v1, v2], p -> %{p | {v1, v2} => p[{v1, v2}] + 1} end)

    {elements, pairs}
  end

  def step(elements, pairs, templ) do
    Enum.reduce(pairs, {elements, pairs}, fn {{v1, v2} = pair, pair_count}, {elems, ps} ->
      e = templ[pair]
      elems = %{elems | e => elems[e] + pair_count}
      ps = %{ps | pair => ps[pair] - pair_count}

      p1 = {v1, e}
      p2 = {e, v2}

      ps = %{ps | p1 => ps[p1] + pair_count}
      ps = %{ps | p2 => ps[p2] + pair_count}

      {elems, ps}
    end)
  end
end
{:module, P2, <<70, 79, 82, 49, 0, 0, 16, ...>>, {:step, 3}}
{elements, pairs} = P2.build_start(start_test, templates_test)

times = 40

{e, p} =
  Enum.reduce(1..times, {elements, pairs}, fn _i, {elems, ps} ->
    P2.step(elems, ps, templates_test)
  end)

min = e |> Enum.min_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)
max = e |> Enum.max_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)

max - min
2188189693529
{elements, pairs} = P2.build_start(start, templates)

times = 40

{e, p} =
  Enum.reduce(1..times, {elements, pairs}, fn _i, {elems, ps} ->
    P2.step(elems, ps, templates)
  end)

min = e |> Enum.min_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)
max = e |> Enum.max_by(fn {_k, v} -> v end) |> then(fn {_k, v} -> v end)

max - min
3459174981021

Correct: 3459174981021