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

Day 12

12.livemd

Day 12

Section

example = """
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
"""

defmodule Day12 do
  def map(input) do
    for {row, y} <- String.split(input, "\n", trim: true) |> Enum.with_index(),
        {p, x} <- String.split(row, "", trim: true) |> Enum.with_index(),
        into: %{} do
      {{x, y, nil}, p}
    end
  end

  def part1(input) do
    map = map(input)

    regions(map, map, [Enum.at(map, 0)], [], [], [])
    |> Enum.map(fn {list, sides} ->
      length(list) * length(sides |> Enum.uniq())
    end)
    |> Enum.sum()
  end

  def part2(input) do
    map = map(input)

    regions(map, map, [Enum.at(map, 0)], [], [], [])
    |> Enum.map(fn {list, sides} ->
      length(list) * (count_v(sides) + count_h(sides) + length(cross(sides)) * 2)
    end)
    |> Enum.sum()
  end

  def cross(sides) do
    sides = sides |> Enum.uniq()

    for {ax, ay, :v} <- sides,
        {bx, by, :v} <- sides,
        {cx, cy, :h} <- sides,
        {dx, dy, :h} <- sides,
        ax == bx,
        by - ay == 1,
        cy == dy,
        dx - cx == 1,
        bx == dx,
        by == dy do
      {cx, ay}
    end
  end

  def count_v(list) do
    list
    |> Enum.uniq()
    |> Enum.filter(&amp;(elem(&amp;1, 2) == :v))
    |> Enum.group_by(&amp;elem(&amp;1, 0), &amp;elem(&amp;1, 1))
    |> Map.values()
    |> Enum.map(fn line ->
      line
      |> Enum.sort()
      |> group([])
      |> length()
    end)
    |> Enum.sum()
  end

  def count_h(list) do
    list
    |> Enum.uniq()
    |> Enum.filter(&amp;(elem(&amp;1, 2) == :h))
    |> Enum.group_by(&amp;elem(&amp;1, 1), &amp;elem(&amp;1, 0))
    |> Map.values()
    |> Enum.map(fn line ->
      line
      |> Enum.sort()
      |> group([])
      |> length()
    end)
    |> Enum.sum()
  end

  def group([], groups), do: groups

  def group([h | t], []) do
    group(t, [[h]])
  end

  def group([h | t], groups) do
    new_groups =
      Enum.map(groups, fn g ->
        if Enum.find(g, fn i -> abs(h - i) == 1 end) do
          [h | g]
        else
          g
        end
      end)

    if groups == new_groups do
      group(t, [[h] | groups])
    else
      group(t, new_groups)
    end
  end

  def regions(map, dmap, [], plots, sides, acc) do
    if p = Enum.at(dmap, 0) do
      regions(map, dmap, [p], [], [], [{plots, sides} | acc])
    else
      [{plots, sides} | acc]
    end
  end

  def regions(map, dmap, [{{x, y, adj}, v} = h | t], plots, sides, acc) do
    case {Map.get(dmap, {x, y, nil}), Map.get(map, {x, y, nil})} do
      {nil, ^v} ->
        regions(map, dmap, t, plots, sides, acc)

      {^v, _} ->
        directions =
          if adj do
            {ladx, lady} = adj
            reverse = [{ladx * -1, lady * -1}]
            [{1, 0}, {-1, 0}, {0, 1}, {0, -1}] -- reverse
          else
            [{1, 0}, {-1, 0}, {0, 1}, {0, -1}]
          end

        pns =
          for {adx, ady} = adj <- directions do
            {{x + adx, y + ady, adj}, v}
          end

        regions(map, Map.delete(dmap, {x, y, nil}), pns ++ t, [h | plots], sides, acc)

      _ ->
        sides =
          case adj do
            {1, 0} ->
              [{x, y, :v} | sides]

            {-1, 0} ->
              [{x + 1, y, :v} | sides]

            {0, 1} ->
              [{x, y, :h} | sides]

            {0, -1} ->
              [{x, y + 1, :h} | sides]

            nil ->
              sides
          end

        regions(map, dmap, t, plots, sides, acc)
    end
  end
end

Day12.part1(example)
Day12.part2(example)