Powered by AppSignal & Oban Pro

Day 18: Snailfish

backup_2021_original/18.livemd

Day 18: Snailfish

Input

input_test =
  """
  [[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]]
  [[[5,[2,8]],4],[5,[[9,9],0]]]
  [6,[[[6,2],[5,6]],[[7,6],[4,7]]]]
  [[[6,[0,7]],[0,9]],[4,[9,[9,0]]]]
  [[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]]
  [[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]]
  [[[[5,4],[7,7]],8],[[8,3],8]]
  [[9,3],[[9,9],[6,[4,9]]]]
  [[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]]
  [[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]
  """
  |> String.split("\n", trim: true)
input =
  File.read!("input18.txt")
  |> String.split("\n", trim: true)

Part 1

defmodule SM do
  def parse(s) when is_bitstring(s), do: parse(String.graphemes(s)) |> elem(0)

  def parse(["[" | t]) do
    {left, ["," | t]} = parse(t)
    {right, ["]" | t]} = parse(t)
    {[left, right], t}
  end

  def parse([x | t]), do: {String.to_integer(x), t}

  def inc([l, r], v, 0), do: [inc(l, v, 0), r]
  def inc([l, r], v, 1), do: [l, inc(r, v, 1)]
  def inc(x, v, _), do: x + v

  def split([l, r]) do
    {l, s} = split(l)

    if not s do
      {r, s} = split(r)
      {[l, r], s}
    else
      {[l, r], s}
    end
  end

  def split(x), do: if(x < 10, do: {x, false}, else: {[trunc(x / 2), x - trunc(x / 2)], true})

  def explode([l, r], 4), do: {true, 0, l, r}

  def explode([l, r], d) do
    {e, l, a, b} = explode(l, d + 1)

    if not e do
      {e, r, a, b} = explode(r, d + 1)

      if e and a > 0 do
        {e, [inc(l, a, 1), r], -1, b}
      else
        {e, [l, r], a, b}
      end
    else
      if b > 0 do
        {e, [l, inc(r, b, 0)], a, -1}
      else
        {e, [l, r], a, b}
      end
    end
  end

  def explode(x, _), do: {false, x, -1, -1}

  def fixup(t) do
    {e, t, _a, _b} = explode(t, 0)

    if not e do
      {t, s} = split(t)
      if s, do: fixup(t), else: t
    else
      fixup(t)
    end
  end

  def mag([l, r]), do: 3 * mag(l) + 2 * mag(r)
  def mag(x), do: x

  def add(b, a), do: fixup([a, b])
  def varsums(l), do: for(a <- l, b <- l, a != b, do: mag(add(a, b)))
end
4140 = Enum.map(input_test, &amp;SM.parse/1) |> Enum.reduce(&amp;SM.add/2) |> SM.mag()
3647 = Enum.map(input, &amp;SM.parse/1) |> Enum.reduce(&amp;SM.add/2) |> SM.mag()

Part 2

3993 = Enum.map(input_test, &amp;SM.parse/1) |> SM.varsums() |> Enum.max()
4600 = Enum.map(input, &amp;SM.parse/1) |> SM.varsums() |> Enum.max()