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

Day 8

2021/day_08.livemd

Day 8

Input

Mix.install([{:kino, github: "livebook-dev/kino"}])
textarea = Kino.Input.textarea("Input:")

Common

defmodule Common do
  def parse_input(raw_input) do
    raw_input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      [lhs, rhs] = String.split(line, "|", trim: true)

      {
        string_to_sets(lhs),
        string_to_sets(rhs)
      }
    end)
  end

  def string_to_sets(string) do
    string
    |> String.split()
    |> Enum.map(&string_to_set/1)
  end

  def string_to_set(string) do
    string
    |> String.split("", trim: true)
    |> Enum.map(&String.to_atom/1)
    |> MapSet.new()
  end
end
raw_input = Kino.Input.read(textarea)
input = Common.parse_input(raw_input)

Part 1

defmodule Part1 do
  def run(input) do
    input
    |> Enum.map(fn {_lhs, rhs} ->
      Enum.count(rhs, fn digit -> Enum.count(digit) in [2, 3, 4, 7] end)
    end)
    |> Enum.sum()
  end
end
Part1.run(input)

Part 2

defmodule Part2 do
  def run(input) do
    Enum.map(input, fn {signals, digits} ->
      segments = [:a, :b, :c, :d, :e, :f, :g]

      d1 = digit1(signals)
      d4 = digit4(signals)
      d7 = digit7(signals)
      d8 = digit8(signals)

      [a] = Enum.to_list(MapSet.difference(d7, d1))

      f =
        Enum.find(segments, fn segment ->
          Enum.count(signals, fn signal ->
            MapSet.member?(signal, segment)
          end) == 9
        end)

      d2 = Enum.find(signals, &(MapSet.member?(&1, f) == false))
      [c] = Enum.to_list(MapSet.delete(d1, f))

      d6 = MapSet.delete(d8, c)

      [b] =
        d8
        |> MapSet.difference(d2)
        |> MapSet.delete(f)
        |> MapSet.to_list()

      [d] =
        d4
        |> MapSet.difference(d1)
        |> MapSet.delete(b)
        |> MapSet.to_list()

      d0 = MapSet.delete(d8, d)

      [d9] =
        signals
        |> Enum.filter(&(&1 not in [d0, d1, d2, d4, d6, d7, d8]))
        |> Enum.filter(&(Enum.count(&1) == 6))

      [e] =
        d8
        |> MapSet.difference(d9)
        |> MapSet.to_list()

      d3 =
        d2
        |> MapSet.delete(e)
        |> MapSet.put(f)

      d5 =
        d8
        |> MapSet.delete(c)
        |> MapSet.delete(e)

      [g] =
        d9
        |> MapSet.difference(d4)
        |> MapSet.delete(a)
        |> MapSet.to_list()

      conversion = %{
        d0 => 0,
        d1 => 1,
        d2 => 2,
        d3 => 3,
        d4 => 4,
        d5 => 5,
        d6 => 6,
        d7 => 7,
        d8 => 8,
        d9 => 9
      }

      digits
      |> Enum.map(fn digit -> conversion[digit] end)
      |> Enum.join()
      |> String.to_integer()
    end)
    |> Enum.sum()
  end

  def digit1(signals), do: find_with_segments_count(signals, 2)
  def digit4(signals), do: find_with_segments_count(signals, 4)
  def digit7(signals), do: find_with_segments_count(signals, 3)
  def digit8(signals), do: find_with_segments_count(signals, 7)

  def diff(digit1, digit2) do
    digit1
    |> MapSet.union(digit2)
    |> MapSet.delete(digit1)
    |> MapSet.delete(digit2)
    |> Enum.count()
  end

  def find_with_segments_count(signals, segments_count) do
    Enum.find(signals, &(Enum.count(&1) == segments_count))
  end
end
Part2.run(input)