bjorng

bjorng

Erlang Core Team

Advent of Code 2021 - Day 10

This topic is about Day 10 of the Advent of Code 2021.

We have a private leaderboard (shared with users of Erlang Forums ):

https://adventofcode.com/2021/leaderboard/private/view/370884

The entry code is:
370884-a6a71927

Most Liked

ruslandoga

ruslandoga

I used a bit of metaprogramming to define the line processor that worked on binaries:

defp process_line(line) do
  process_line(line, _stack = [])
end

for <<open, close>> <- ["[]", "()", "{}", "<>"] do
  defp process_line(<<unquote(close), rest::bytes>>, [unquote(close) | stack]) do
    process_line(rest, stack)
  end

  defp process_line(<<unquote(open), rest::bytes>>, stack) do
    process_line(rest, [unquote(close) | stack])
  end
end

defp process_line(<<>>, []), do: :valid
defp process_line(<<>>, stack), do: {:incomplete, stack}
defp process_line(<<char, _rest::bytes>>, _stack), do: {:corrupted, char}

Full solution

mexicat

mexicat

Today’s problem seemed like a good match for Elixir.

defmodule AdventOfCode.Day10 do
  @chunks %{
    "(" => ")",
    "[" => "]",
    "{" => "}",
    "<" => ">"
  }

  def part1(input) do
    input
    |> parse_input()
    |> Enum.map(&eval_line/1)
    |> Enum.filter(&(elem(&1, 0) == :error))
    |> Enum.map(fn {_, v} -> v end)
    |> Enum.sum()
  end

  def part2(input) do
    results =
      input
      |> parse_input()
      |> Enum.map(&eval_line/1)
      |> Enum.filter(&(elem(&1, 0) == :ok))
      |> Enum.map(fn {_, v} -> v end)
      |> Enum.sort()

    middle = results |> length() |> div(2)
    Enum.at(results, middle)
  end

  def eval_line(line, open \\ [])

  def eval_line([head | tail], open) when head in ["(", "[", "{", "<"] do
    eval_line(tail, [head | open])
  end

  def eval_line([head | tail], [open_head | open_tail]) do
    if head == @chunks[open_head] do
      eval_line(tail, open_tail)
    else
      {:error, score_error(head)}
    end
  end

  def eval_line(_, open) do
    score =
      Enum.reduce(open, 0, fn char, acc ->
        acc * 5 + score_ac(@chunks[char])
      end)

    {:ok, score}
  end

  def score_error(")"), do: 3
  def score_error("]"), do: 57
  def score_error("}"), do: 1197
  def score_error(">"), do: 25137

  def score_ac(")"), do: 1
  def score_ac("]"), do: 2
  def score_ac("}"), do: 3
  def score_ac(">"), do: 4

  def parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&String.codepoints/1)
  end
end
ruslandoga

ruslandoga

I think it is equivalent except that my implementation for part2 needs to be updated to score openers instead of closers (since stack contains openers for incomplete lines). I rerun the tests with these two changes and they passed.

As for your original question,

Any reason it is better to match on insertion into the stack rather than on popping off the stack?

I don’t think I had any particular reason, at the moment it just made sense to add closers to the stack.

fabionebbia

fabionebbia

I think today’s puzzle was the one I enjoyed the most.
Here’s my solution:

defmodule Day10 do
  defguardp is_opening(paren) when paren in [?(, ?[, ?{, ?<]

  defguardp matches(opening, closing) when opening == ?( and closing == ?)
  defguardp matches(opening, closing) when opening in [?[, ?{, ?<] and closing == opening + 2

  @illegal_points %{
    ?) => 3,
    ?] => 57,
    ?} => 1197,
    ?> => 25137
  }

  @incomplete_points %{
    ?) => 1,
    ?] => 2,
    ?} => 3,
    ?> => 4
  }

  @doc """
      iex>Day10.part1(\"""
      ...>[({(<(())[]>[[{[]{<()<>>
      ...>[(()[<>])]({[<{<<[]>>(
      ...>{([(<{}[<>[]}>{[]{[(<()>
      ...>(((({<>}<{<{<>}{[]{[]{}
      ...>[[<[([]))<([[{}[[()]]]
      ...>[{[{({}]{}}([{[{{{}}([]
      ...>{<[[]]>}<{[{[{[]{()[[[]
      ...>[<(<(<(<{}))><([]([]()
      ...><{([([[(<>()){}]>(<<{{
      ...><{([{{}}[<[[[<>{}]]]>[]]
      ...>\""")
      26397
  """
  def part1(input) do
    input
    |> parse_input()
    |> Enum.map(fn subsystem -> visit(subsystem, []) end)
    |> Enum.filter(&(elem(&1, 0) == :illegal))
    |> Enum.map(fn {_, paren} -> @illegal_points[paren] end)
    |> Enum.sum()
  end

  @doc """
      iex>Day10.part2(\"""
      ...>[({(<(())[]>[[{[]{<()<>>
      ...>[(()[<>])]({[<{<<[]>>(
      ...>{([(<{}[<>[]}>{[]{[(<()>
      ...>(((({<>}<{<{<>}{[]{[]{}
      ...>[[<[([]))<([[{}[[()]]]
      ...>[{[{({}]{}}([{[{{{}}([]
      ...>{<[[]]>}<{[{[{[]{()[[[]
      ...>[<(<(<(<{}))><([]([]()
      ...><{([([[(<>()){}]>(<<{{
      ...><{([{{}}[<[[[<>{}]]]>[]]
      ...>\""")
      288957
  """
  def part2(input) do
    points =
      input
      |> parse_input()
      |> Enum.map(fn subsystem -> visit(subsystem, []) end)
      |> Enum.filter(&(elem(&1, 0) == :incomplete))
      |> Enum.map(fn {_, expected} ->
        expected
        |> Enum.map(&@incomplete_points[&1])
        |> Enum.reduce(0, fn points, score ->
          score * 5 + points
        end)
      end)
      |> Enum.sort()

    Enum.at(points, div(length(points), 2))
  end

  defp visit([opening, closing | input], expected) when matches(opening, closing) do
    visit(input, expected)
  end

  defp visit([closing | input], [closing | expected]) do
    visit(input, expected)
  end

  defp visit([opening | input], expected) when is_opening(opening) do
    visit(input, [pair(opening) | expected])
  end

  defp visit([illegal | _], _), do: {:illegal, illegal}
  defp visit([], expected), do: {:incomplete, expected}

  defp pair(?(), do: ?)
  defp pair(paren) when paren in [?[, ?{, ?<], do: paren + 2

  defp parse_input(input) when is_binary(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&to_charlist/1)
  end
end
stevensonmt

stevensonmt

Nice use of guards. I always forget about that aspect of Elixir.

Where Next?

Popular in Challenges Top

LostKobrakai
This topic is about Day 9 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
bismark
Took me a minute to remember my binary math :smile: :grimacing:… import Bitwise __DIR__ |&gt; Path.join("puzzle.txt") |&gt; File.stream...
New
Aetherus
This topic is about the Advent of Code 2021 - Day 4. Thanks to @bjorng , we now have a new Private Leaderboard. The entry code is: 370...
New
bjorng
This topic is about Day 18 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adve...
New
igorb
I found today a bit tedious: advent-of-code-2024/lib/advent_of_code2024/day15.ex at main · ibarakaiev/advent-of-code-2024 · GitHub.
New
bjorng
Note: This topic is to talk about Day 2 of the Advent of Code 2019 . There is a private leaderboard for elixirforum members. You can joi...
New
Aetherus
This topic is about Day 4 of the Advent of Code 2020 . Thanks to @egze, we have a private leaderboard: https://adventofcode.com/2020/le...
New
rugyoga
Fairly straightforward Dijkstra’s algorithm import AOC aoc 2023, 17 do def compute(input, candidates) do {{max_row, max_col}, ite...
New
bjorng
This topic is about Day 6 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums ): https://adve...
New
New

Other popular topics Top

Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3268 119930 1237
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
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
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
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
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

We're in Beta

About us Mission Statement