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

bismark
Took me a minute to remember my binary math :smile: :grimacing:… import Bitwise __DIR__ |&gt; Path.join("puzzle.txt") |&gt; File.stream...
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
Aetherus
This topic is about Day 5 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
shritesh
I mapped both the cards and every possible hand to numeric values and sorted them. In part 2 I could only think of replacing the jokers w...
New
bjorng
This topic is about Day 1 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adven...
New
christhekeele
Setting this down for the night, as after a quick naive solve for quick part 1 I realize that part 2 is by design computationally expensi...
New
adamu
Probably not the most efficient implementation, because part 1 took &gt;1 ms and part 2 &gt;4ms, but the code was simple enough. def p...
New
connorlay
Note by the Moderators: This topic is for general discussion about the Advent of Code 2018. To prevent people from being spoiled about s...
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
cblavier
Hi, there :wave: Today, I felt it was way more challenging! I went through part2 thanks to Agent based memoization (without memoization ...
New

Other popular topics Top

William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
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
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
273 38985 115
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New

We're in Beta

About us Mission Statement