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

--- Day 8: Seven Segment Search ---

2021/elixir/day-08/day-08.livemd

— Day 8: Seven Segment Search —

Mix.install([{:kino, "~> 0.9.4"}])

Section

https://www.twitch.tv/videos/1228693946?collection=k_DLnk2tvBa-fQ

https://adventofcode.com/2021/day/8

input = Kino.Input.textarea("test input")

Part 1

part1 =
  Kino.Input.read(input)
  |> String.split("\n", trim: true)
  |> Enum.map(&String.split(&1, " | ", trim: true))
  |> Enum.map(&Enum.at(&1, 1))
  |> Enum.join(" ")
  |> String.split(" ", trim: true)
  |> Enum.map(&String.length(&1))
  |> Enum.filter(&(&1 in [2, 3, 4, 7]))
  |> Enum.frequencies()
  |> Map.values()
  # 239
  |> Enum.sum()

Part 2

# My fundamental mistake here - I decided to focus on decrypting each segment
# but should've focused on working with the whole number.
# ie I went one level deeper than it was required. And this made the whole
# thing more complicated.
defmodule Segments1 do
  @master_key %{
    "abcefg" => 0,
    "cf" => 1,
    "acdeg" => 2,
    "acdfg" => 3,
    "bcdf" => 4,
    "abdfg" => 5,
    "abdefg" => 6,
    "acf" => 7,
    "abcdefg" => 8,
    "abcdfg" => 9
  }

  @all ["abcefg", "cf", "cadeg", "acdfg", "bcdf", "abdfg", "abdefg", "acf", "abcdefg", "abcdfg"]

  @test [[Enum.join(@all, " "), Enum.join(@all, " ")]]

  def diff(str1, str2) do
    MapSet.difference(MapSet.new(str1), MapSet.new(str2))
    |> MapSet.to_list()
  end

  def find_by_length(list, length) do
    Enum.filter(list, &(String.length(&1) == length))
    |> Enum.map(&String.split(&1, "", trim: true))
  end

  def a(solution, segments) do
    seven = find_by_length(segments, 3) |> List.flatten()
    one = find_by_length(segments, 2) |> List.flatten()
    Map.put(solution, "a", diff(seven, one) |> Enum.at(0))
  end

  def b(solution, segments) do
    four = find_by_length(segments, 4) |> List.flatten()
    one = find_by_length(segments, 2) |> List.flatten()
    Map.put(solution, "b", diff(four, one) |> Enum.at(0))
  end

  def c(solution, segments) do
    # c = not in [0,6,9] # now we know 6
    sixers = find_by_length(segments, 6)
    one = find_by_length(segments, 2) |> List.flatten()

    [c] =
      sixers
      |> Enum.map(&Segments1.diff(one, &1))
      |> List.flatten()

    Map.put(solution, "c", c)
  end

  def f(solution, segments) do
    # f = 1 - c
    one = find_by_length(segments, 2) |> List.flatten()
    c = Map.get(solution, "c")
    Map.put(solution, "f", diff(one, [c]) |> Enum.at(0))
  end

  def d(solution, segments) do
    # d = 4 - 1 - b
    four = find_by_length(segments, 4) |> List.flatten()
    one = find_by_length(segments, 2) |> List.flatten()
    b = Map.get(solution, "b")
    Map.put(solution, "d", diff(four, one) |> diff([b]) |> Enum.at(0))
  end

  def e(solution, segments) do
    # e = [2,3,5] - ([0,9] - 6)
    fivers = find_by_length(segments, 5)
    sixers = find_by_length(segments, 6)
    one = find_by_length(segments, 2) |> List.flatten()
    six = six(sixers, one)
    zero_nine = sixers |> Enum.filter(&(&1 !== six))

    de =
      zero_nine
      |> Enum.map(&Segments1.diff(six, &1))
      |> List.flatten()

    [e] =
      fivers
      |> Enum.map(&diff(de, &1))
      |> List.flatten()
      |> Enum.uniq()

    Map.put(solution, "e", e)
  end

  def g(solution, segments) do
    [g] =
      ["a", "b", "c", "d", "e", "f", "g"]
      |> diff(Map.values(solution))

    # Map.put(solution, "g", Enum.at(g, 0))
    Map.put(solution, "g", g)
  end

  def translate_segment(segment, key_map) do
    # key must be already reversed
    segment
    |> Enum.map(&Map.get(key_map, &1))
    |> Enum.sort()
    |> Enum.join()
  end

  def segment_to_num(segment) do
    Map.get(@master_key, segment)
    |> Integer.to_string()
  end

  def six(sixers, one) do
    c =
      sixers
      |> Enum.map(&Segments1.diff(one, &1))
      |> List.flatten()
      |> Enum.at(0)

    sixers
    |> Enum.filter(&(c not in &1))
    |> List.flatten()
  end

  def reverse_map(map) do
    map
    |> Map.to_list()
    |> Enum.map(&{elem(&1, 1), elem(&1, 0)})
    |> Map.new()
  end

  def solve(input), do: Enum.map(input, &solver/1)

  def solver([segments, puzzle]) do
    # ["acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab", "cdfeb fcadb cdfeb cdbaf"]
    segments = String.split(segments, " ")

    key =
      %{}
      |> Segments1.a(segments)
      |> Segments1.b(segments)
      |> Segments1.c(segments)
      |> Segments1.f(segments)
      |> Segments1.d(segments)
      |> Segments1.e(segments)
      |> Segments1.g(segments)
      |> Segments1.reverse_map()

    puzzle
    |> String.split(" ")
    |> Enum.map(&(String.split(&1, "", trim: true) |> Enum.sort()))
    |> Enum.map(&(Segments1.translate_segment(&1, key) |> Segments1.segment_to_num()))
    |> Enum.join()
  end

  def test, do: solve(@test)
end

input
|> Segments1.solve()

José

input = Kino.Input.textarea("test")
input =
  Kino.Input.read(input)
  |> String.split([" |\n", "\n", " | "], trim: true)
  |> Enum.chunk_every(2)
  |> Enum.map(fn [input, output] ->
    {String.split(input, " ") |> Enum.map(&String.to_charlist/1),
     String.split(output, " ") |> Enum.map(&String.to_charlist/1)}
  end)
  |> List.first()
do_stuff = fn input ->
  [one] = Enum.filter(elem(input, 0), &(length(&1) == 2))
  [four] = Enum.filter(elem(input, 0), &(length(&1) == 4))
  [seven] = Enum.filter(elem(input, 0), &(length(&1) == 3))
  [eight] = Enum.filter(elem(input, 0), &(length(&1) == 7))
  zero_six_nine = Enum.filter(elem(input, 0), &(length(&1) == 6))
  [nine] = Enum.filter(zero_six_nine, &match?([], four -- &1))
  [zero] = Enum.filter(zero_six_nine -- [nine], &match?([], one -- &1))
  [six] = Enum.filter(zero_six_nine -- [nine], &match?([_], one -- &1))
  two_three_five = Enum.filter(elem(input, 0), &(length(&1) == 5))
  [three] = Enum.filter(two_three_five, &match?([], one -- &1))
  [five] = Enum.filter(two_three_five -- [three], &match?([_], nine -- &1))
  [two] = two_three_five -- [three, five]

  {zero, one, two, three, four, five, six, seven, eight, nine}

  map = %{
    Enum.sort(zero) => 0,
    Enum.sort(one) => 1,
    Enum.sort(two) => 2,
    Enum.sort(three) => 3,
    Enum.sort(four) => 4,
    Enum.sort(five) => 5,
    Enum.sort(six) => 6,
    Enum.sort(seven) => 7,
    Enum.sort(eight) => 8,
    Enum.sort(nine) => 9
  }

  elem(input, 1)
  |> Enum.map(&Map.get(map, Enum.sort(&1)))
  |> Integer.undigits()
end

input
|> Enum.map(&do_stuff.(&1))
|> Enum.sum()