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

Day 08

2021/day-08.livemd

Day 08

Setup

Mix.install([{:kino, "~> 0.4.1"}])
example_input =
  Kino.Input.textarea("example input:")
  |> Kino.render()

real_input = Kino.Input.textarea("real input:")

Part 1

# 1  4  7  8
known_digit_lengths = [2, 4, 3, 7]

real_input
|> Kino.Input.read()
|> String.split(["\n", " | "])
|> Enum.drop_every(2)
|> Enum.flat_map(&String.split/1)
|> Enum.map(&String.length/1)
|> Enum.frequencies()
|> Map.take(known_digit_lengths)
|> Map.values()
|> Enum.sum()

Part 2

defmodule Entry do
  @chars ~W[a b c d e f g]

  @one ~W[c f]
  @four ~W[b c d f]
  @seven ~W[a c f]

  @outputs %{
    (@chars -- ~W[d]) => 0,
    @one => 1,
    (@chars -- ~W[b f]) => 2,
    (@chars -- ~W[b e]) => 3,
    @four => 4,
    (@chars -- ~W[c e]) => 5,
    (@chars -- ~W[c]) => 6,
    @seven => 7,
    @chars => 8,
    (@chars -- ~W[e]) => 9
  }

  defp chars(s), do: String.split(s, "", trim: true)

  def correct(decoder, outputs) do
    outputs
    |> Enum.map(fn string ->
      String.split(string, "", trim: true)
      |> Enum.map(&Map.fetch!(decoder, &1))
      |> Enum.sort()
      |> then(&Map.fetch!(@outputs, &1))
    end)
    |> Integer.undigits()
  end

  def decode(signals, mapping) do
    possible_bs = find_b(signals, MapSet.new(@chars))
    possible_gs = find_g(signals, MapSet.new(@chars))

    mapping
    |> decode_signals(signals)
    |> deduce(possible_bs, possible_gs)
    |> Enum.map(fn {k, [v]} -> {v, k} end)
    |> Map.new()
  end

  defp decode_signals(mapping, []), do: mapping

  defp decode_signals(mapping, [<> | rest]) do
    mangled_chars = chars(value)

    put_decoder(mapping, @one, mangled_chars)
    |> update_decoder(@one, mangled_chars)
    |> decode_signals(rest)
  end

  defp decode_signals(mapping, [<> | rest]) do
    update_decoder(mapping, @four, chars(value))
    |> decode_signals(rest)
  end

  defp decode_signals(mapping, [<> | rest]) do
    update_decoder(mapping, @seven, chars(value))
    |> decode_signals(rest)
  end

  defp decode_signals(mapping, [_ | rest]), do: decode_signals(mapping, rest)

  # this function is not pretty....
  defp deduce(orig, possible_bs, possible_gs) do
    mapping =
      orig
      |> Map.update!("b", &amp;(MapSet.intersection(MapSet.new(&amp;1), possible_bs) |> MapSet.to_list()))
      |> Map.update!("f", &amp;(MapSet.intersection(MapSet.new(&amp;1), possible_bs) |> MapSet.to_list()))
      |> Map.update!("g", &amp;(MapSet.intersection(MapSet.new(&amp;1), possible_gs) |> MapSet.to_list()))

    mapping
    |> Map.update!("a", &amp;(&amp;1 -- mapping["e"]))
    |> Map.update!("b", &amp;(&amp;1 -- mapping["g"]))
    |> Map.update!("c", &amp;(&amp;1 -- mapping["f"]))
    |> Map.update!("d", &amp;(&amp;1 -- mapping["b"]))
    |> Map.update!("d", &amp;(&amp;1 -- mapping["e"]))
    |> Map.update!("e", &amp;(&amp;1 -- mapping["g"]))
  end

  defp find_b([], possibles), do: possibles

  defp find_b([<> | rest], possibles) do
    find_b(rest, MapSet.intersection(MapSet.new(chars(value)), possibles))
  end

  defp find_b([_ | rest], possibles), do: find_b(rest, possibles)

  defp find_g([], possibles), do: possibles

  defp find_g([<> | rest], possibles) do
    find_g(rest, MapSet.intersection(MapSet.new(chars(value)), possibles))
  end

  defp find_g([_ | rest], possibles), do: find_g(rest, possibles)

  defp put_decoder(mapping, known_chars, mangled_chars) do
    known_chars
    |> Enum.reduce(mapping, fn key, map ->
      Map.put(map, key, mangled_chars)
    end)
  end

  defp update_decoder(mapping, known_chars, mangled_chars) do
    @chars
    |> Kernel.--(known_chars)
    |> Enum.reduce(mapping, fn key, map ->
      Map.update!(map, key, &amp;(&amp;1 -- mangled_chars))
    end)
  end
end
initial_mapping =
  ~W[a b c d e f g]
  |> then(fn list ->
    list
    |> Enum.zip(Stream.repeatedly(fn -> list end))
    |> Map.new()
  end)

real_input
|> Kino.Input.read()
|> String.split(["\n", " | "])
|> Enum.map(&amp;String.split/1)
|> Enum.chunk_every(2)
|> Enum.map(fn [signals, outputs] ->
  Entry.decode(signals, initial_mapping)
  |> Entry.correct(outputs)
end)
|> Enum.sum()