Powered by AppSignal & Oban Pro

Day 8

2021/notebooks/day-08.livemd

Day 8

Setup

input = Aoc.get_input(8)
textarea = Kino.Input.textarea("Puzzle input", default: input)
test_textarea = Kino.Input.textarea("Test input")
small_test_textarea = Kino.Input.textarea("Small test input")
options = [
  puzzle: "Puzzle",
  test: "Test",
  small_test: "Small"
]

select = Kino.Input.select("Input source", options)
lines =
  select
  |> Kino.Input.read()
  |> case do
    :puzzle -> input
    :test -> test_textarea |> Kino.Input.read()
    :small_test -> small_test_textarea |> Kino.Input.read()
  end
  |> String.split(["\n"], trim: true)
  |> Enum.map(&(String.trim(&1) |> String.split(" | ") |> Enum.map(fn s -> String.split(s) end)))

Part 1

digit_to_count =
  [6, 2, 5, 5, 4, 5, 6, 3, 7, 6]
  |> Enum.with_index()
  |> Enum.map(fn {a, b} -> {b, a} end)
  |> Enum.into(%{})
count_to_digits =
  digit_to_count
  |> Enum.reduce(%{}, fn {v, c}, acc ->
    Map.update(acc, c, [v], &[v | &1])
  end)
  |> IO.inspect(charlists: :as_lists)
lines
|> Enum.flat_map(&Enum.at(&1, 1))
|> IO.inspect()
|> Enum.map(&String.length/1)
|> IO.inspect()
|> Enum.filter(&(&1 in [2, 3, 4, 7]))
|> Enum.count()

Part 2

pop_pattern = fn patterns, len ->
  pattern = Enum.find(patterns, &(String.length(&1) == len))
  patterns = Enum.reject(patterns, &(&1 == pattern))
  {patterns, pattern}
end

sort = fn str -> String.split(str, "", trim: true) |> Enum.sort() |> Enum.join("") end

lines
|> Enum.map(fn [patterns, output] ->
  # %{2 => [1], 3 => [7], 4 => [4], 5 => [5, 3, 2], 6 => [9, 6, 0], 7 => [8]}
  {patterns, pattern1} = pop_pattern.(patterns, 2)
  {patterns, pattern4} = pop_pattern.(patterns, 4)
  {patterns, pattern7} = pop_pattern.(patterns, 3)
  {patterns, pattern8} = pop_pattern.(patterns, 7)

  # %{5 => [5, 3, 2], 6 => [9, 6, 0]}
  parts4 = String.split(pattern4, "", trim: true)

  pattern9 =
    patterns
    |> Enum.filter(&(String.length(&1) == 6))
    |> Enum.find(fn p ->
      parts = String.split(p, "", trim: true)
      parts4 |> Enum.all?(&(&1 in parts))
    end)

  patterns = Enum.reject(patterns, &(&1 == pattern9))

  # %{5 => [5, 3, 2], 6 => [6, 0]}
  # now that we have 9, we can find 2
  # take 8 and subtract all letters of 9, the letter that's left in the 5 group will be a 2
  parts8 = String.split(pattern8, "", trim: true)
  parts9 = String.split(pattern9, "", trim: true)

  [letter] = Enum.reject(parts8, &(&1 in parts9))

  pattern2 =
    patterns |> Enum.filter(&(String.length(&1) == 5)) |> Enum.find(&String.contains?(&1, letter))

  patterns = Enum.reject(patterns, &(&1 == pattern2))

  parts2 = String.split(pattern2, "", trim: true)
  parts2_minus_letter = Enum.reject(parts2, &(&1 == letter))

  pattern3 =
    patterns
    |> Enum.filter(&(String.length(&1) == 5))
    |> Enum.find(fn p ->
      parts = String.split(p, "", trim: true)

      parts2_minus_letter
      |> Enum.all?(&(&1 in parts))
    end)

  patterns = Enum.reject(patterns, &(&1 == pattern3))
  {patterns, pattern5} = pop_pattern.(patterns, 5)

  parts1 = String.split(pattern1, "", trim: true)

  pattern0 =
    Enum.find(patterns, fn p ->
      parts = String.split(p, "", trim: true)
      parts1 |> Enum.all?(&(&1 in parts))
    end)

  patterns = Enum.reject(patterns, &(&1 == pattern0))

  [pattern6] = patterns

  mapping =
    %{}
    |> Map.put(pattern1, 1)
    |> Map.put(pattern4, 4)
    |> Map.put(pattern7, 7)
    |> Map.put(pattern8, 8)
    |> Map.put(pattern9, 9)
    |> Map.put(pattern2, 2)
    |> Map.put(pattern3, 3)
    |> Map.put(pattern5, 5)
    |> Map.put(pattern0, 0)
    |> Map.put(pattern6, 6)

  {patterns, mapping}

  # output converted to number ie. 5353
  mapping = mapping |> Enum.map(fn {k, v} -> {sort.(k), v} end) |> Enum.into(%{})

  output
  |> Enum.map(&sort.(&1))
  |> Enum.map(&Map.get(mapping, &1))
  |> Enum.join("")
  |> String.to_integer()
end)
|> Enum.sum()