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

Day 12

day12.livemd

Day 12

Setup

input =
  System.fetch_env!("LB_AOC_DIR")
  |> Path.join("data/day12.txt")
  |> File.read!()

nil
nil

Solve

defmodule Day12 do
  @directions [
    {0, 1},
    {0, -1},
    {1, 0},
    {-1, 0}
  ]

  def parse(input) do
    for {row, i} <- Enum.with_index(String.split(input, "\n")),
        row != "",
        {item, j} <- Enum.with_index(String.codepoints(row)),
        into: %{} do
      {{i, j}, item}
    end
  end

  def flood({i, j} = current, board, group) do
    for {di, dj} <- @directions,
        Map.get(board, current) == Map.get(board, {i + di, j + dj}),
        !MapSet.member?(group, {i + di, j + dj}),
        reduce: group do
      acc ->
        group = MapSet.put(acc, {i + di, j + dj})
        flood({i + di, j + dj}, board, group)
    end
  end

  defp perimeter(region, board) do
    for {i, j} <- region, {di, dj} <- @directions, reduce: 0 do
      sum ->
        if Map.get(board, {i, j}) == Map.get(board, {i + di, j + dj}), do: sum, else: sum + 1
    end
  end

  def sides(region, board) do
    all_sides =
      for {i, j} <- region, {di, dj} <- @directions, reduce: MapSet.new() do
        s ->
          if Map.get(board, {i, j}) == Map.get(board, {i + di, j + dj}) do
            s
          else
            MapSet.put(s, {{i, j}, {di, dj}})
          end
      end

    for {i, j} <- region,
        {{^i, ^j}, {di, dj} = direction} <-
          MapSet.filter(all_sides, &amp;match?({{^i, ^j}, _direction}, &amp;1)),
        reduce: 0 do
      sum ->
        if MapSet.member?(all_sides, {{i - abs(dj), j - abs(di)}, direction}),
          do: sum,
          else: sum + 1
    end
  end

  def regions(board) do
    for space <- Map.keys(board), reduce: [] do
      groups ->
        if Enum.any?(groups, &amp;MapSet.member?(&amp;1, space)) do
          groups
        else
          [flood(space, board, MapSet.new([space])) | groups]
        end
    end
  end

  @doc ~S"""
  iex> Day12.part1("RRRRIICCFF\nRRRRIICCCF\nVVRRRCCFFF\nVVRCCCJFFF\nVVVVCJJCFE\nVVIVCCJJEE\nVVIIICJJEE\nMIIIIIJJEE\nMIIISIJEEE\nMMMISSJEEE\n")
  1930
  """
  def part1(input) do
    board =
      parse(input)

    regions(board)
    |> Enum.reduce(
      0,
      fn region, sum ->
        sum + Enum.count(region) * perimeter(region, board)
      end
    )
  end

  @doc ~S"""
  iex> Day12.part2("RRRRIICCFF\nRRRRIICCCF\nVVRRRCCFFF\nVVRCCCJFFF\nVVVVCJJCFE\nVVIVCCJJEE\nVVIIICJJEE\nMIIIIIJJEE\nMIIISIJEEE\nMMMISSJEEE\n")
  1206
  """
  def part2(input) do
    board =
      parse(input)

    regions(board)
    |> Enum.reduce(
      0,
      fn region, sum ->
        sum + Enum.count(region) * sides(region, board)
      end
    )
  end
end
{:module, Day12, <<70, 79, 82, 49, 0, 0, 27, ...>>, {:part2, 1}}