rugyoga

rugyoga

Advent of Code 2021 - Day 8

part 1:

part 2:

Most Liked

hauleth

hauleth

Thanks @josevalim for custom operators. Again - LiveBook:


Day 8

input =
  File.stream!("day8.txt")
  |> Stream.map(fn line ->
    line
    |> String.split(" | ")
    |> Enum.map(fn part ->
      part
      |> String.trim()
      |> String.split(" ")
      |> Enum.map(fn disp ->
        disp
        |> String.to_charlist()
        |> Enum.sort()
        |> List.to_string()
      end)
    end)
    |> List.to_tuple()
  end)
#Stream<[
  enum: %File.Stream{
    line_or_bytes: :line,
    modes: [:raw, :read_ahead, :binary],
    path: "day8.txt",
    raw: true
  },
  funs: [#Function<47.58486609/1 in Stream.map/2>]
]>
input
|> Enum.map(fn {_, output} ->
  Enum.count(output, &(byte_size(&1) in [2, 3, 4, 7]))
end)
|> Enum.sum()
390
defmodule Day8.Task2 do
  def a --- b do
    MapSet.difference(a, b)
  end

  def a +++ b do
    MapSet.union(a, b)
  end

  def a <~> b do
    MapSet.intersection(a, b)
  end

  def a <|> b do
    (a +++ b) --- (a <~> b)
  end

  #            1.   7.    4. 2|3|5. 2|3|5. 2|3|5.  6|9|0.  6|9|0.  6|9|0.       8.
  def deduce({cf, acf, bcdf, acdeg, acdfg, abdfg, abdefg, abcdfg, abcefg, abcdefg}) do
    a = acf --- cf
    eg = abcdefg --- (acf +++ bcdf)
    bd = bcdf --- cf
    abfg = abdefg <|> abcdfg <|> abcefg
    b = abfg <~> bd
    f = abfg <~> cf
    g = abfg --- (a +++ b +++ f)
    d = bd --- b
    c = cf --- f
    e = eg --- g

    {a, b, c, d, e, f, g} =
      [a, b, c, d, e, f, g]
      |> Enum.map(&extract/1)
      |> List.to_tuple()

    [
      # 0
      [a, b, c, e, f, g],
      # 1
      [c, f],
      # 2
      [a, c, d, e, g],
      # 3
      [a, c, d, f, g],
      # 4
      [b, c, d, f],
      # 5
      [a, b, d, f, g],
      # 6
      [a, b, d, e, f, g],
      # 7
      [a, c, f],
      # 8
      [a, b, c, d, e, f, g],
      # 9
      [a, b, c, d, f, g]
    ]
    |> Enum.map(&List.to_string(Enum.sort(&1)))
    |> Enum.with_index()
    |> Map.new()
  end

  defp extract(a) do
    [v] = MapSet.to_list(a)

    v
  end

  def decode(matches, output) do
    output
    |> Enum.map(&matches[&1])
    |> Integer.undigits()
  end
end

input
|> Enum.map(fn {input, output} ->
  input
  |> Enum.sort_by(&byte_size/1)
  |> Enum.map(&MapSet.new(String.to_charlist(&1)))
  |> List.to_tuple()
  |> Day8.Task2.deduce()
  |> Day8.Task2.decode(output)
end)
|> Enum.sum()
warning: variable "abdfg" is unused (if the variable is not meant to be used, prefix it with an underscore)
  solutions.livemd#cell:19: Day8.Task2.deduce/1

warning: variable "acdeg" is unused (if the variable is not meant to be used, prefix it with an underscore)
  solutions.livemd#cell:19: Day8.Task2.deduce/1

warning: variable "acdfg" is unused (if the variable is not meant to be used, prefix it with an underscore)
  solutions.livemd#cell:19: Day8.Task2.deduce/1

1011785
mruoss

mruoss

Second part approach:

  • Get the correct mapping from segments to digit
    • 1, 4, 7 and 8 are unique by the number of segments
    • 6, 9, 0 all have 6 segments
      • derive 9 which is the only one that fully contains 4
      • derive 0 which is the only one that fully contains 7
      • 6 is the remainder of the 6-segment digits
    • 2, 3, 5 all have 5 segments
      • derive 3 which is the only one that fully contains 7
      • derive 5 which is the only one fully contained in 6
      • 2 is the remainder of the 5-segment digits
  def solve(stream, :second) do
    stream
    |> parse() # returns a tuple {patterns, output}, both being sorted charlists
    |> Stream.map(&derive_numbers/1)
    |> Enum.sum()
  end

  defp derive_numbers({patterns, output}) do
    %{2 => [one], 3 => [seven], 4 => [four], 5 => len_five, 6 => len_six, 7 => [eight]} =
      (output ++ patterns)
      |> Enum.uniq()
      |> Enum.group_by(&Kernel.length/1)

    {[nine],  len_six}  = Enum.split_with(len_six,  fn nr -> four -- nr == [] end)
    {[zero],  [six]}    = Enum.split_with(len_six,  fn nr -> seven -- nr == [] end)
    {[three], len_five} = Enum.split_with(len_five, fn nr -> seven -- nr == [] end)
    {[five],  [two]}    = Enum.split_with(len_five, fn nr -> nr -- six == [] end)

    output
    |> Enum.map(fn
      ^one -> 1
      ^two -> 2
      ^three -> 3
      ^four -> 4
      ^five -> 5
      ^six -> 6
      ^seven -> 7
      ^eight -> 8
      ^nine -> 9
      ^zero -> 0
    end)
    |> Integer.undigits()
  end
josevalim

josevalim

Creator of Elixir

Day 8 was my favorite day by far, here is my solution: aoc/day-08.livemd at main · josevalim/aoc · GitHub

Ended up using a lot of pattern matching and charlists. Streaming here: Twitch (there is a discussion about bytes, charlists, and Unicode at the end).

groovyda

groovyda

I went down the same route initially - of looking segment-by-segment. But somewhere I realised that I just need the numbers themselves - and then I modified the code.

That was definitely a red herring - given how much time they spent explaining the segments and matching them…

Used a very similar approach to @mruoss - but used MapSet

defmodule Day8 do
  def input2nums(input) do
    segs = 
      input
      |> String.split
      |> Enum.map(&String.to_charlist/1)
      |> Enum.map(&MapSet.new/1)
      |> Enum.sort_by(&MapSet.size/1)

    one = Enum.at(segs, 0)
    seven = Enum.at(segs, 1)
    four = Enum.at(segs, 2)
    eight = Enum.at(segs, 9)
    possible_253 = [Enum.at(segs, 3), Enum.at(segs, 4), Enum.at(segs, 5)]
    possible_069 = [Enum.at(segs, 6), Enum.at(segs, 7), Enum.at(segs, 8)]
    [six] = 
      Enum.filter(
        possible_069, 
        fn num -> 1 == MapSet.intersection(num, one) |> MapSet.size end)
    [three] = 
      Enum.filter(
        possible_253,
        fn num -> 2 == MapSet.intersection(num, one) |> MapSet.size end)
    [nine] = 
      Enum.filter(
        possible_069,
        fn num -> MapSet.intersection(num, three) == three end)
    [zero] = possible_069 -- [six, nine]
    [two] = 
      Enum.filter(
        possible_253,
        fn num -> 2 == MapSet.intersection(num, four) |> MapSet.size end)
    [five] = possible_253 -- [two, three]
    [zero, one, two, three, four, five, six, seven, eight, nine]
  end

  def output2num(line, nums_set) do
    line = 
      line
      |> String.split
      |> Enum.map(&String.to_charlist/1)
      |> Enum.map(&MapSet.new/1)
    
    out = for x <- line, do: Enum.find_index(nums_set, &(&1 == x))
    Enum.join(out)
    |> String.to_integer
  end

  def convert_line(input, output) do
    nums_set = input2nums(input)
    output2num(output, nums_set)
  end
end
stefanluptak

stefanluptak

Part 2 was really interesting today.
I am bit afraid of what’s ahead of us. :smiley:

Where Next?

Popular in Challenges Top

bjorng
Note: This topic is to talk about Day 16 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
dominicletz
This topic is about Day 8 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
LostKobrakai
This one has been quite the ride. Struggled at first to find a good data format to suite the problem. I really like how that turned out b...
New
groovyda
Today’s challenge for me was about using reduce: defmodule Prob5 do def move([[h1 | rest] = _list1, list2]) do [rest, [h1 | list2]...
New
Aetherus
This topic is about Day 7 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
bjorng
Note: This topic is to talk about Day 23 of the Advent of Code. For general discussion about the Advent of Code 2018 and links to topics...
New
New
lud
Gosh this one took me sooo much time. At first I was trying to iterate each digit independently on the input A number to make digits cha...
New
bjorng
This topic is about Day 10 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums ): https://adv...
New
mattbaker
I’m having so much fun working on the “Protohackers” challenges, I never got into Advent of Code much but this has been amazing. The chal...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement