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", &(MapSet.intersection(MapSet.new(&1), possible_bs) |> MapSet.to_list()))
|> Map.update!("f", &(MapSet.intersection(MapSet.new(&1), possible_bs) |> MapSet.to_list()))
|> Map.update!("g", &(MapSet.intersection(MapSet.new(&1), possible_gs) |> MapSet.to_list()))
mapping
|> Map.update!("a", &(&1 -- mapping["e"]))
|> Map.update!("b", &(&1 -- mapping["g"]))
|> Map.update!("c", &(&1 -- mapping["f"]))
|> Map.update!("d", &(&1 -- mapping["b"]))
|> Map.update!("d", &(&1 -- mapping["e"]))
|> Map.update!("e", &(&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, &(&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(&String.split/1)
|> Enum.chunk_every(2)
|> Enum.map(fn [signals, outputs] ->
Entry.decode(signals, initial_mapping)
|> Entry.correct(outputs)
end)
|> Enum.sum()