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

Day 4: Scratchcards

2023/day_04.livemd

Day 4: Scratchcards

Mix.install([:kino])

input = Kino.Input.textarea("Please paste your input:")

Part 1

Run in Livebook

https://adventofcode.com/2023/day/4

data =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
cards =
  data
  |> Enum.map(fn card_str ->
    %{"winning_numbers_str" => winning_numbers_str, "numbers_str" => numbers_str} =
      Regex.named_captures(
        ~r/Card\s+\d+:(?.+)\|(?.+)/,
        card_str
      )

    winning_numbers =
      winning_numbers_str
      |> String.split(" ", trim: true)
      |> Enum.map(&String.to_integer/1)

    numbers =
      numbers_str
      |> String.split(" ", trim: true)
      |> Enum.map(&String.to_integer/1)

    %{winning_numbers: winning_numbers, numbers: numbers}
  end)

cards
|> Enum.map(fn %{winning_numbers: winning_numbers, numbers: numbers} ->
  match_count =
    MapSet.intersection(MapSet.new(winning_numbers), MapSet.new(numbers))
    |> Enum.count()

  case match_count do
    0 -> 0
    match_count -> 2 ** (match_count - 1)
  end
end)
|> Enum.sum()

Part 2

https://adventofcode.com/2023/day/4#part2

{_, card_count} =
  cards
  |> Enum.with_index()
  |> Enum.reduce({%{}, 0}, fn {%{winning_numbers: winning_numbers, numbers: numbers}, i},
                              {copies, sum} ->
    match_count =
      MapSet.intersection(MapSet.new(winning_numbers), MapSet.new(numbers))
      |> Enum.count()

    card_count = Map.get(copies, i, 0) + 1

    new_copies =
      1..match_count//1
      |> Enum.reduce(copies, fn x, copies ->
        copies |> Map.update(i + x, card_count, &(&1 + card_count))
      end)

    new_sum = sum + card_count

    {new_copies, new_sum}
  end)

card_count