Powered by AppSignal & Oban Pro

Day 8

day_8.livemd

Day 8

Setup

Mix.install([
  {:kino, "~> 0.4.1"}
])
input = Kino.Input.textarea("Please provide your input: ")
input =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(&String.split(&1, " | "))

Part 1

defmodule AdventOfCode.DayEight do
  def solve_part_one(input) do
    input
    |> parse_v1()
    |> Enum.filter(fn item ->
      item
      |> String.length()
      |> simple_digit?()
    end)
    |> Enum.count()
  end

  defp simple_digit?(7), do: true
  defp simple_digit?(4), do: true
  defp simple_digit?(3), do: true
  defp simple_digit?(2), do: true
  defp simple_digit?(_x), do: false

  defp parse_v1(input) do
    input
    |> Enum.flat_map(fn [_list, number] ->
      String.split(number, " ", trim: true)
    end)
  end

  def solve_part_two(input) do
    input
    |> parse()
    |> Enum.reduce(0, fn [input, number], acc ->
      acc + letters_to_numbers(input, number)
    end)
  end

  defp parse(input) do
    input
    |> Enum.map(fn [list, number] ->
      [String.split(list, " ", trim: true), String.split(number, " ", trim: true)]
    end)
  end

  defp sort_by_charlist(input) do
    input
    |> to_charlist()
    |> Enum.sort()
    |> List.to_string()
  end

  defp letters_to_numbers(input, number) do
    %{}
    |> add_one(input)
    |> add_four(input)
    |> add_seven(input)
    |> add_eight(input)
    |> add_six_nine_zero(input)
    |> add_five_three_two(input)
    |> parse_number(number)
  end

  defp parse_number(map, number) do
    number
    |> Enum.map(fn el -> sort_by_charlist(el) end)
    |> Enum.reduce("", fn el, acc ->
      acc <> Integer.to_string(Map.get(map, el))
    end)
    |> String.to_integer()
  end

  defp add_one(map, input) do
    one =
      input
      |> Enum.find("one", fn el -> String.length(el) == 2 end)
      |> sort_by_charlist()

    Map.merge(map, %{
      one => 1
    })
  end

  defp add_four(map, input) do
    four =
      input
      |> Enum.find("four", fn el -> String.length(el) == 4 end)
      |> sort_by_charlist()

    Map.merge(map, %{
      four => 4
    })
  end

  defp add_seven(map, input) do
    seven =
      input
      |> Enum.find("seven", fn el -> String.length(el) == 3 end)
      |> sort_by_charlist()

    Map.merge(map, %{
      seven => 7
    })
  end

  defp add_eight(map, input) do
    eight =
      input
      |> Enum.find("eight", fn el -> String.length(el) == 7 end)
      |> sort_by_charlist()

    Map.merge(map, %{
      eight => 8
    })
  end

  def add_six_nine_zero(map, input) do
    one = number_to_mapset(map, 1)
    four = number_to_mapset(map, 4)

    six_nine_zero =
      input
      |> Enum.filter(fn item -> item |> String.length() == 6 end)
      |> Enum.map(fn encoded ->
        encoded = sort_by_charlist(encoded)

        charlist =
          encoded
          |> to_charlist()
          |> MapSet.new()

        cond do
          MapSet.difference(one, charlist) |> MapSet.size() > 0 ->
            %{encoded => 6}

          MapSet.difference(four, charlist) |> MapSet.size() > 0 ->
            %{encoded => 0}

          true ->
            %{encoded => 9}
        end
      end)
      |> Enum.reduce(%{}, fn el, acc -> Map.merge(acc, el) end)

    Map.merge(map, six_nine_zero)
  end

  def add_five_three_two(map, input) do
    seven = number_to_mapset(map, 7)
    nine = number_to_mapset(map, 8)

    five_three_two =
      input
      |> Enum.filter(fn item -> item |> String.length() == 5 end)
      |> Enum.map(fn encoded ->
        encoded = sort_by_charlist(encoded)

        charlist =
          encoded
          |> to_charlist()
          |> MapSet.new()

        cond do
          MapSet.difference(charlist, nine) |> MapSet.size() == 1 ->
            %{encoded => 2}

          MapSet.difference(charlist, seven) |> MapSet.size() == 3 ->
            %{encoded => 5}

          true ->
            %{encoded => 3}
        end
      end)
      |> Enum.reduce(%{}, fn el, acc -> Map.merge(acc, el) end)

    Map.merge(map, five_three_two)
  end

  defp number_to_mapset(map, target) do
    map
    |> Map.filter(fn {_k, v} -> v == target end)
    |> Enum.map(fn {k, _v} -> k end)
    |> Enum.at(0)
    |> to_charlist()
    |> MapSet.new()
  end
end

# AdventOfCode.DayEight.solve_part_one(input)
AdventOfCode.DayEight.solve_part_two(input)