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

Day 04

2023/day04.livemd

Day 04

Mix.install(kino: "~> 0.11")

Common

import Kino.Shorts

input = read_textarea("Puzzle input", monospace: true)
lines = String.split(input, "\n", trim: true)
cards = Enum.map(lines, &Card.parse/1)
defmodule Card do
  defstruct [:winning, :numbers]

  def parse(entry) do
    [_card_str, all_numbers] = String.split(entry, ":")

    [winning, numbers] =
      all_numbers
      |> String.split("|", parts: 2)
      |> Enum.map(&parse_numbers/1)

    %__MODULE__{
      winning: winning,
      numbers: numbers
    }
  end

  defp parse_numbers(numbers) do
    numbers
    |> String.split()
    |> Enum.map(&String.to_integer/1)
  end

  def count_winning(card) do
    Enum.count(card.numbers, fn number ->
      number in card.winning
    end)
  end
end

Part 1

cards
|> Stream.map(&Card.count_winning/1)
|> Stream.map(fn
  0 -> 0
  count -> Integer.pow(2, count - 1)
end)
|> Enum.sum()

Part 2

default_multipliers = List.duplicate(1, Enum.count(cards))

cards
|> Stream.transform(default_multipliers, fn card, [multiplier | others] ->
  count_winning = Card.count_winning(card)

  {to_update, staying} = Enum.split(others, count_winning)
  updated = Enum.map(to_update, &(&1 + multiplier))

  {[multiplier], updated ++ staying}
end)
|> Enum.sum()