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

Advent 2023 - Day 4

day4.livemd

Advent 2023 - Day 4

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

Section

input = Kino.Input.textarea("Please paste your input file")
input =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(fn card ->
    [card_num, rest] = String.split(card, ": ")

    card_num =
      card_num
      |> String.trim_leading("Card")
      |> String.replace(" ", "")
      |> Integer.parse()
      |> elem(0)

    [winning, have] = String.split(rest, " | ")

    strip = fn strip ->
      strip
      |> String.split(" ")
      |> Enum.filter(&(&1 != ""))
      |> Enum.map(&Integer.parse/1)
      |> Enum.map(&elem(&1, 0))
      |> MapSet.new()
    end

    winning = strip.(winning)
    have = strip.(have)

    {card_num, winning, have}
  end)
[
  {1, MapSet.new([17, 41, 48, 83, 86]), MapSet.new([6, 9, 17, 31, 48, 53, 83, 86])},
  {2, MapSet.new([13, 16, 20, 32, 61]), MapSet.new([17, 19, 24, 30, 32, 61, 68, 82])},
  {3, MapSet.new([1, 21, 44, 53, 59]), MapSet.new([1, 14, 16, 21, 63, 69, 72, 82])},
  {4, MapSet.new([41, 69, 73, 84, 92]), MapSet.new([5, 51, 54, 58, 59, 76, 83, 84])},
  {5, MapSet.new([26, 28, 32, 83, 87]), MapSet.new([12, 22, 30, 36, 70, 82, 88, 93])},
  {6, MapSet.new([13, 18, 31, 56, 72]), MapSet.new([10, 11, 23, 35, 36, 67, 74, 77])}
]
cards =
  for {card, winning, have} <- input do
    count =
      for pick <- have do
        MapSet.member?(winning, pick)
      end
      |> Enum.filter(&amp; &amp;1)
      |> Enum.count()

    {card, count}
  end
[{1, 4}, {2, 2}, {3, 2}, {4, 1}, {5, 0}, {6, 0}]

Part 1

for {_card, count} <- cards do
  score = Integer.pow(2, count) / 2
  floor(score)
end
|> Enum.sum()
13

Part 2

defmodule Scratchcards do
  def duplicate(cards) do
    duplicate(0, cards)
  end

  def duplicate(index, cards) do
    card = Map.get(cards, index)

    if is_nil(card) do
      cards
    else
      {winning, copies} = card

      if winning == 0 do
        duplicate(index + 1, cards)
      else
        range = (index + 1)..(index + winning)

        cards =
          Enum.reduce(range, cards, fn index, cards ->
            if Map.has_key?(cards, index) do
              Map.get_and_update!(cards, index, fn {other_winning, other_copies} = existing ->
                {existing, {other_winning, copies + other_copies}}
              end)
              |> elem(1)
            else
              cards
            end
          end)

        duplicate(index + 1, cards)
      end
    end
  end
end
{:module, Scratchcards, <<70, 79, 82, 49, 0, 0, 11, ...>>, {:duplicate, 2}}
cards
|> Enum.map(fn {card_num, winning} -> {card_num - 1, {winning, 1}} end)
|> Map.new()
|> Scratchcards.duplicate()
|> Map.values()
|> Enum.map(&amp;elem(&amp;1, 1))
|> Enum.sum()
30