Powered by AppSignal & Oban Pro

Day 8

livebook/day-08.livemd

Day 8

Setup

| be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe | | edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc | | fgaebd cg bdaec gdafb agbcfd gdcbef bgcad gfac gcb cdgabef | cg cg fdcagb cbg | | fbegcd cbd adcefb dageb afcb bc aefdc ecdab fgdeca fcdbega | efabcd cedba gadfec cb | | aecbfdg fbg gf bafeg dbefa fcge gcbea fcaegb dgceab fcbdga | gecf egdcabf bgf bfgea | | fgeab ca afcebg bdacfeg cfaedg gcfdb baec bfadeg bafgc acf | gebdcfa ecba ca fadegcb | | dbcfg fgd bdegcaf fgec aegbdf ecdfab fbedc dacgb gdcebf gf | cefg dcbef fcge gbcadfe | | bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbgef | | egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb | | gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce |

Mix.install(
  [
    {:kino, "~> 0.4.1"}
  ],
  consolidate_protocols: false
)

input = Kino.Input.textarea("Please paste your input:")
defmodule Clean do
  def split_and_sort(string_list) do
    string_list
    |> String.split(" ", trim: true)
    |> Enum.map(fn string ->
      string
      |> String.to_charlist()
      |> Enum.sort()
    end)
  end
end

lines =
  input
  |> Kino.Input.read()
  |> String.split(["\n", "|"], trim: true)
  |> Enum.chunk_every(2)
  |> Enum.map(fn [signal, output] ->
    {Clean.split_and_sort(signal), Clean.split_and_sort(output)}
  end)

Part 1

lines
|> Enum.map(fn {_signal, output} ->
  Enum.map(output, &Enum.count/1)
  |> Enum.count(fn length -> length in [2, 3, 4, 7] end)
end)
|> Enum.sum()

Part 2 🙈

be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb |
fdgacbe cefdb cefbgd gcbe
  {['be', 'abcdefg', 'bcdefg', 'acdefg', 'bceg', 'cdefg', 'abdefg', 'bcdef', 'abcdf', 'bde'],
   ['abcdefg'8, 'bcdef'3, 'bcdefg'9, 'bceg']4}

 be
 edb
 abcdefg
 cgeb
 cdefg
 abcdf
 bcdef
 abdefg
 bcdefg
 acdefg

known   dcfgeb
unknown a

          1234567
Length  | Signal | Integer | Pattern
2           b  e   1          3  6   <>
3         d b  e   7 -----  1 3  6   <>
4          gbc e   4         234 6   <>
7         dgbcaef  8 -----  1234567  <> 
5         d bca f  2        1 345 7
5         d bc ef  3 -----  1 34 67
5         dgbc ef  5        12 4 67
6         dgb aef  0 -----  123 567
6         dgbcaef  6        12 4567
6         dgbc ef  9 -----  1234 67  
- pos 4 (search [4,7,5]   occurs in all of them)
- pos 1 (search [2,3,4,7] occurs in 3 and 7 only)
- pos 7 (search [7,5,6]   occurs in all of them)
- pos 6 (search [2,3,4,6] occurs everywhere AND in TWO [5])
- pos 3 (search [3]       only one unknown remaining)
- pos 5 (search [7,6] occurs everywhere and just in ONE [5])
- pos 2 (last unknown)

['abefg', 'ac', 'abcefg', 'abcdefg', 'acdefg', 'bcdfg', 'abce', 'abdefg', 'abcfg', 'acf']

ac acf abce abcdefg

abefg bcdfg abcfg

abcefg acdefg abdefg

acdefg

      1_34567

Length | Signal | Integer | Pattern 2 c a 1 3 6 <> 3 f c a 7 ----- 1 3 6 <> 4 dcb ag 4 234 6 <> 7 fdcbeag 8 ----- 1234567 <> 5 f cbe g 2 1 345 7 5 f cb ag 3 ----- 1 34 67 5 fd b ag 5 12 4 67 6 fdc eag 0 ----- 123 567 6 fd beag 6 12 4567 6 fdcb ag 9 ----- 1234 67

  {['abcdefg', 'bcdef', 'acdfg', 'abcdf', 'abd', 'abcdef', 'bcdefg', 'abef', 'abcdeg', 'ab'],
   ['bcdef'5, 'abcdf'3, 'bcdef'5, 'abcdf'3]}

ab
abd  
abef
abcdefg

bcdef
acdfg
abcdf

abcdef
bcdefg
abcdeg


dfceba
g
          1  4  7
Length  | Signal | Integer | Pattern
2           a  b    1          3  6   <>
3         d a  b    7 -----  1 3  6   <>
4          eaf b    4         234 6   <>
7         deafgbc   8 -----  1234567  <> 
5         d afg c   2        1 345 7
5         d af bc   3 -----  1 34 67
5         de f bc   5        12 4 67
6         dea gbc   0 -----  123 567
6         de fgbc   6        12 4567
6         deaf bc   9 -----  1234 67  


Actual
  0:(6)   1:(2)   2:(5)   3:(5)   4:(4)
 1111    ....    1111    1111    ....
2    3  .    3  .    3  .    3  2    3
2    3  .    3  .    3  .    3  2    3
 ....    ....    4444    4444    4444
5    6  .    6  5    .  .    6  .    6
5    6  .    6  5    .  .    6  .    6
 7777    ....    7777    7777    ....

  5:(5)   6:(6)   7:(3)   8:(7)   9:(6)
 1111    1111    1111    1111    1111
2    .  2    .  .    3  2    3  2    3
2    .  2    .  .    3  2    3  2    3
 4444    4444    ....    4444    4444
.    6  5    6  .    6  5    6  .    6
.    6  5    6  .    6  5    6  .    6
 7777    7777    ....    7777    7777


Template
  0:      1:      2:      3:      4:
 1111    ....    1111    1111    ....
2    3  .    3  .    3  .    3  2    3
2    3  .    3  .    3  .    3  2    3
 ....    ....    4444    4444    4444
5    6  .    6  5    .  .    6  .    6
5    6  .    6  5    .  .    6  .    6
 7777    ....    7777    7777    ....

  5:      6:      7:      8:      9:
 1111    1111    1111    1111    1111
2    .  2    .  .    3  2    3  2    3
2    .  2    .  .    3  2    3  2    3
 4444    4444    ....    4444    4444
.    6  5    6  .    6  5    6  .    6
.    6  5    6  .    6  5    6  .    6
 7777    7777    ....    7777    7777

# - pos 4 (search [4,7,5]   occurs in all of them)
# - pos 1 (search [2,3,4,7] occurs in 3 and 7 only)
# - pos 7 (search [7,5,6]   occurs in all of them)
# - pos 6 (search [2,3,4,6] occurs everywhere AND in TWO [5])
# - pos 3 (search [3]       only one unknown remaining)
# - pos 5 (search [7,6] occurs everywhere and just in ONE [5])
# - pos 2 (last unknown)

defmodule Part2 do
  def unique_chars_in_signals(signals) do
    x =
      Enum.reduce(signals, [], fn chars, uniq_chars -> '#{chars}#{uniq_chars}' |> Enum.uniq() end)

    # IO.puts(Enum.to_list(x))
    x
  end

  def pos4({_, _, _unknown_chars = [], _}) do
    raise "UHO.. pos4 did not find anything"
  end

  def pos4({signals, {p1, p2, p3, p4, p5, p6, p7}, [char | unknown_chars], remaining_chars}) do
    if Part2.char_present_in_all_lists(char, signals[5] ++ signals[4] ++ signals[7]) do
      remaining_chars =
        remaining_chars
        |> List.delete_at(Enum.find_index(remaining_chars, &(char == &1)))

      {signals, {p1, p2, p3, char, p5, p6, p7}, remaining_chars, remaining_chars}
    else
      Part2.pos4({signals, {p1, p2, p3, p4, p5, p6, p7}, unknown_chars, remaining_chars})
    end
  end

  def pos1({_, _, _unknown_chars = [], _}) do
    raise "UHO.. pos1 did not find anything"
  end

  def pos1({signals, {p1, p2, p3, p4, p5, p6, p7}, [char | unknown_chars], remaining_chars}) do
    if Part2.char_present_in_all_lists(char, signals[3] ++ signals[7]) and
         Part2.char_not_present(char, signals[2] ++ signals[4]) do
      remaining_chars =
        remaining_chars
        |> List.delete_at(Enum.find_index(remaining_chars, &(char == &1)))

      {signals, {char, p2, p3, p4, p5, p6, p7}, remaining_chars, remaining_chars}
    else
      Part2.pos1({signals, {p1, p2, p3, p4, p5, p6, p7}, unknown_chars, remaining_chars})
    end
  end

  def pos7({_, _, _unknown_chars = [], _}) do
    raise "UHO.. pos7 did not find anything"
  end

  def pos7({signals, {p1, p2, p3, p4, p5, p6, p7}, [char | unknown_chars], remaining_chars}) do
    if Part2.char_present_in_all_lists(char, signals[5] ++ signals[6] ++ signals[7]) do
      remaining_chars =
        remaining_chars
        |> List.delete_at(Enum.find_index(remaining_chars, &(char == &1)))

      {signals, {p1, p2, p3, p4, p5, p6, char}, remaining_chars, remaining_chars}
    else
      Part2.pos7({signals, {p1, p2, p3, p4, p5, p6, p7}, unknown_chars, remaining_chars})
    end
  end

  def pos2({_, _, _unknown_chars = [], _}) do
    raise "UHO.. pos2 did not find anything"
  end

  def pos2({signals, {p1, p2, p3, p4, p5, p6, p7}, [char | unknown_chars], remaining_chars}) do
    if Part2.char_present_in_all_lists(char, signals[4] ++ signals[7]) and
         Part2.char_present_in_one_list_only(char, signals[5]) do
      remaining_chars =
        remaining_chars
        |> List.delete_at(Enum.find_index(remaining_chars, &(char == &1)))

      {signals, {p1, char, p3, p4, p5, p6, p7}, remaining_chars, remaining_chars}
    else
      Part2.pos2({signals, {p1, p2, p3, p4, p5, p6, p7}, unknown_chars, remaining_chars})
    end
  end

  def pos6({_, _, _unknown_chars = [], _}) do
    raise "UHO.. pos6 did not find anything"
  end

  def pos6({signals, {p1, p2, p3, p4, p5, p6, p7}, [char | unknown_chars], remaining_chars}) do
    # IO.puts(
    #   "pos6 #{inspect(signals[2] ++ signals[3] ++ signals[4] ++ signals[5] ++ signals[6] ++ signals[7])} #{List.to_string([char])} #{unknown_chars}"
    # )
    if Part2.char_present_in_all_lists(
         char,
         signals[2] ++ signals[3] ++ signals[4] ++ signals[6]
       ) and
         Part2.char_present_in_two_lists(char, signals[5]) do
      remaining_chars =
        remaining_chars
        |> List.delete_at(Enum.find_index(remaining_chars, &(char == &1)))

      {signals, {p1, p2, p3, p4, p5, char, p7}, remaining_chars, remaining_chars}
    else
      Part2.pos6({signals, {p1, p2, p3, p4, p5, p6, p7}, unknown_chars, remaining_chars})
    end
  end

  def pos3({_, _, _unknown_chars = [], _}) do
    raise "UHO.. pos3 did not find anything"
  end

  def pos3({signals, {p1, p2, p3, p4, p5, p6, p7}, [char | unknown_chars], remaining_chars}) do
    if char in List.first(signals[3]) do
      remaining_chars =
        remaining_chars
        |> List.delete_at(Enum.find_index(remaining_chars, &(char == &1)))

      {signals, {p1, p2, char, p4, p5, p6, p7}, remaining_chars, remaining_chars}
    else
      Part2.pos3({signals, {p1, p2, p3, p4, p5, p6, p7}, unknown_chars, remaining_chars})
    end
  end

  def pos5({_, _, _unknown_chars = [], _}) do
    raise "UHO.. pos5 did not find anything"
  end

  def pos5({signals, {p1, p2, p3, p4, p5, p6, p7}, [char | unknown_chars], remaining_chars}) do
    if Part2.char_present_in_all_lists(
         char,
         signals[7] ++ signals[6]
       ) and
         Part2.char_present_in_one_list_only(char, signals[5]) do
      remaining_chars =
        remaining_chars
        |> List.delete_at(Enum.find_index(remaining_chars, &(char == &1)))

      {signals, {p1, p2, p3, p4, char, p6, p7}, remaining_chars, remaining_chars}
    else
      Part2.pos5({signals, {p1, p2, p3, p4, p5, p6, p7}, unknown_chars, remaining_chars})
    end
  end

  def char_present_in_one_list_only(char, lists) do
    Enum.count(lists, fn list -> char in list end) == 1
  end

  def char_present_in_two_lists(char, lists) do
    Enum.count(lists, fn list -> char in list end) == 2
  end

  def char_present_in_all_lists_except_one(char, lists) do
    IO.puts("char_present_in_all_lists_except_one")
    IO.puts(List.to_string([char]))
    IO.puts(inspect(lists))
    Enum.count(lists, fn list -> [char] in list end) == 2
  end

  def char_present_in_all_lists(char, lists) do
    Enum.all?(lists, &(char in &1))
  end

  def char_not_present(char, lists) do
    Enum.all?(lists, &(char not in &1))
  end

  def wire_connections({p1, p2, p3, p4, p5, p6, p7}) do
    IO.puts({p1, p2, p3, p4, p5, p6, p7} |> Tuple.to_list())

    %{
      Enum.sort([p1, p2, p3, p5, p6, p7]) => 0,
      Enum.sort([p3, p6]) => 1,
      Enum.sort([p1, p3, p4, p5, p7]) => 2,
      Enum.sort([p1, p3, p4, p6, p7]) => 3,
      Enum.sort([p2, p3, p4, p6]) => 4,
      Enum.sort([p1, p2, p4, p6, p7]) => 5,
      Enum.sort([p1, p2, p4, p5, p6, p7]) => 6,
      Enum.sort([p1, p3, p6]) => 7,
      Enum.sort([p1, p2, p3, p4, p5, p6, p7]) => 8,
      Enum.sort([p1, p2, p3, p4, p6, p7]) => 9
    }
  end

  def get_config_from_signals(signals, config, unknown_chars) do
    signals
    |> Enum.group_by(&Enum.count/1)
    |> (fn signals -> Part2.pos4({signals, config, unknown_chars, unknown_chars}) end).()
    |> Part2.pos1()
    |> Part2.pos7()
    |> Part2.pos6()
    |> Part2.pos3()
    |> Part2.pos5()
    |> (fn {signals, {p1, _, p3, p4, p5, p6, p7}, unknown_chars, _remaining_chars} ->
          IO.puts(inspect(signals))
          {p1, unknown_chars |> List.first(), p3, p4, p5, p6, p7}
        end).()
    |> Part2.wire_connections()
  end
end

# [{signals, output} | _] = lines

# Part2.unique_chars_in_signals([
#   'be',
#   'abcdefg',
#   'bcdefg',
#   'acdefg',
#   'bceg',
#   'cdefg',
#   'abdefg',
#   'bcdef',
#   'abcdf',
#   'bde'
# ])

# config =
#   Part2.get_config_from_signals(
#     signals,
#     {1, 2, 3, 4, 5, 6, 7, 8, 9},
#     Part2.unique_chars_in_signals(signals)
#   )
[lines | _] = lines

configs =
  [lines]
  |> Enum.map(fn {signals, outputs} ->
    config =
      Part2.get_config_from_signals(
        signals,
        {1, 2, 3, 4, 5, 6, 7},
        Part2.unique_chars_in_signals(signals)
      )

    IO.puts("Config #{inspect(config)}")

    Enum.map(outputs, fn output ->
      IO.puts(inspect(output))
      Map.get(config, output)
    end)
  end)

{['be', 'abcdefg', 'bcdefg', 'acdefg', 'bceg', 'cdefg', 'abdefg', 'bcdef', 'abcdf', 'bde'], ['abcdefg', 'bcdef', 'bcdefg', 'bceg']},

{d, a, b, c, g, e, f' [p1, p2, p3,}

Template 0: 1: 2: 3: 4: 1111 .... 1111 1111 .... 2 3 . 3 . 3 . 3 2 3 2 3 . 3 . 3 . 3 2 3 .... .... 4444 4444 4444 5 6 . 6 5 . . 6 . 6 5 6 . 6 5 . . 6 . 6 7777 .... 7777 7777 ....

5: 6: 7: 8: 9: 1111 1111 1111 1111 1111 2 . 2 . . 3 2 3 2 3 2 . 2 . . 3 2 3 2 3 4444 4444 .... 4444 4444 . 6 5 6 . 6 5 6 . 6 . 6 5 6 . 6 5 6 . 6 7777 7777 .... 7777 7777

Part 2 José

parsed =
  input
  |> Kino.Input.read()
  |> String.split([" |\n", " | ", "\n"], trim: true)
  |> Enum.chunk_every(2)
  |> Enum.map(fn [input, output] ->
    {input |> String.split() |> Enum.group_by(&byte_size/1, &String.to_charlist/1),
     output |> String.split() |> Enum.map(&String.to_charlist/1)}
  end)
supersets = fn numbers, pivot ->
  Enum.filter(numbers, &match?([], pivot -- &1))
end

almost_supersets = fn numbers, pivot ->
  Enum.filter(numbers, &match?([_], pivot -- &1))
end

parsed
|> Enum.map(fn {input, output} ->
  %{
    2 => [one],
    3 => [seven],
    4 => [four],
    5 => two_three_five,
    6 => zero_six_nine,
    7 => [eight]
  } = input

  [nine] = supersets.(zero_six_nine, four)
  [six] = almost_supersets.(zero_six_nine, seven)
  [zero] = zero_six_nine -- [six, nine]

  [three] = supersets.(two_three_five, seven)
  [five] = almost_supersets.(two_three_five, six)
  [two] = two_three_five -- [three, five]

  numbers = %{
    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
  }

  [d1, d2, d3, d4] = output

  Integer.undigits([
    numbers[Enum.sort(d1)],
    numbers[Enum.sort(d2)],
    numbers[Enum.sort(d3)],
    numbers[Enum.sort(d4)]
  ])
end)
|> Enum.sum()