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, "~> 0.11.3"}])

Day 4

sample_input = Kino.Input.textarea("Paste Sample Input")
real_input = Kino.Input.textarea("Paste Real Input")
defmodule Scratchcard do
  defstruct id: 0, winners: MapSet.new(), draws: MapSet.new(), points: 0

  def parse("Card   " <> card_text), do: parse("Card #{card_text}")

  def parse("Card  " <> card_text), do: parse("Card #{card_text}")

  def parse("Card " <> card_text) do
    {card_no, ": " <> number_text} = Integer.parse(card_text)
    [win_text, draw_text] = String.split(number_text, "|", trim: true)

    %__MODULE__{
      id: card_no,
      winners: parse_number_list(win_text),
      draws: parse_number_list(draw_text)
    }
  end

  def match_count(%__MODULE__{winners: winners, draws: draws}) do
    winners
    |> MapSet.intersection(draws)
    |> MapSet.size()
  end

  def score(%__MODULE__{} = card) do
    case match_count(card) do
      0 -> 0
      n -> 2 ** (n - 1)
    end
  end

  defp parse_number_list(text) do
    text
    |> String.split(~r/\s+/, trim: true)
    |> Enum.map(&amp;String.to_integer/1)
    |> MapSet.new()
  end
end
part_1 = fn input ->
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&amp;Scratchcard.parse/1)
  |> Enum.map(&amp;Scratchcard.score/1)
  |> Enum.sum()
end
part_1.(sample_input)
part_1.(real_input)
part_2 = fn input ->
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&amp;Scratchcard.parse/1)
  |> Enum.reduce({0, %{}}, fn card, {total, winnings} ->
    this_card_count = 1 + Map.get(winnings, card.id, 0)
    match_count = Scratchcard.match_count(card)

    updated_winnings =
      Enum.reduce(1..match_count//1, winnings, fn offset, prev_winnings ->
        Map.update(prev_winnings, card.id + offset, this_card_count, &amp;(this_card_count + &amp;1))
      end)

    {total + this_card_count, updated_winnings}
  end)
  |> elem(0)
end
part_2.(sample_input)
part_2.(real_input)