Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 03

2022/elixir/day03.livemd

Day 03

Mix.install([
  {:kino, "~> 0.7.0"}
])

Puzzle Input

area = Kino.Input.textarea("Puzzle Input")
puzzle_input = Kino.Input.read(area)
example_input = """
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw
"""

Common

defmodule Items do
  def parse_rucksack(str), do: String.codepoints(str)

  def parse_compartments(rucksack) do
    count = round(length(rucksack) / 2)
    Enum.split(rucksack, count)
  end

  def common_compartments_item({left, right}) do
    [item] =
      MapSet.intersection(
        MapSet.new(left),
        MapSet.new(right)
      )
      |> MapSet.to_list()

    item
  end

  @scores Map.merge(
            ?a..?z |> Stream.map(&to_string([&1])) |> Stream.zip(1..26) |> Map.new(),
            ?A..?Z |> Stream.map(&to_string([&1])) |> Stream.zip(27..52) |> Map.new()
          )

  def item_priority(item) do
    Map.get(@scores, item, 0)
  end

  def group_token(rs) do
    [token] =
      rs
      |> Stream.map(&MapSet.new/1)
      |> Enum.reduce(&MapSet.intersection(&1, &2))
      |> MapSet.to_list()

    token
  end
end
ExUnit.start(auto_run: false)

defmodule ItemsTests do
  use ExUnit.Case, async: true
  import Items

  test "rucksack parsing" do
    assert parse_rucksack("vJrwpWtwJgWrhcsFMMfFFhFp") ==
             ~w(v J r w p W t w J g W r h c s F M M f F F h F p)
  end

  test "compartments parsing" do
    rucksack = ~w(v J r w p W t w J g W r h c s F M M f F F h F p)

    assert parse_compartments(rucksack) == {
             ~w(v J r w p W t w J g W r),
             ~w(h c s F M M f F F h F p)
           }
  end

  test "common compartments item" do
    compartments = {~w(v J r w p W t w J g W r), ~w(h c s F M M f F F h F p)}
    assert common_compartments_item(compartments) == "p"
  end

  test "item priority" do
    assert item_priority("p") == 16
    assert item_priority("L") == 38
  end

  test "group token" do
    group = [
      ~w(v J r w p W t w J g W r h c s F M M f F F h F p),
      ~w(j q H R N q R j q z j G D L G L r s F M f F Z S r L r F Z s S L),
      ~w(P m m d z q P r V v P w w T W B w g)
    ]

    assert group_token(group) == "r"
  end
end

ExUnit.run()
rucksacks =
  puzzle_input
  |> String.split("\n", trim: true)
  |> Stream.map(&Items.parse_rucksack/1)

Part One

rucksacks
|> Stream.map(&Items.parse_compartments/1)
|> Stream.map(&Items.common_compartments_item/1)
|> Stream.map(&Items.item_priority/1)
|> Enum.sum()
rucksacks
|> Task.async_stream(fn rucksack ->
  rucksack
  |> Items.parse_compartments()
  |> Items.common_compartments_item()
  |> Items.item_priority()
end)
|> Enum.reduce(0, fn {:ok, priority}, acc -> priority + acc end)

Part Two

rucksacks
|> Stream.chunk_every(3)
|> Stream.map(&Items.group_token/1)
|> Stream.map(&Items.item_priority/1)
|> Enum.sum()
rucksacks
|> Stream.chunk_every(3)
|> Task.async_stream(fn group ->
  group
  |> Items.group_token()
  |> Items.item_priority()
end)
|> Enum.reduce(0, fn {:ok, priority}, acc -> priority + acc end)