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

Day4

day4/sol.livemd

Day4

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

Part 1

input = Kino.Input.textarea("Please enter the input here:")
defmodule Day4Part1 do
  def solve(input) do
    String.split(input, "\n", trim: true)
    |> Enum.map(fn line ->
      [_, numbers] = String.split(line, ": ")
      [winning | [have]] = String.split(numbers, " | ", trim: true)
      winning = String.split(winning, " ", trim: true) |> Enum.map(&String.to_integer/1)
      have = String.split(have, " ", trim: true) |> Enum.map(&String.to_integer/1)
      find_score(winning, have)
    end)
    |> Enum.sum()
  end

  defp find_score(winning, have) do
    set =
      Enum.reduce(winning, MapSet.new(), fn x, acc ->
        MapSet.put(acc, x)
      end)

    winning_cards = Enum.filter(have, fn x -> MapSet.member?(set, x) end)
    len = length(winning_cards)

    case len do
      0 -> 0
      x -> 2 ** (x - 1)
    end
  end
end
{:module, Day4Part1, <<70, 79, 82, 49, 0, 0, 12, ...>>, {:find_score, 2}}
input
|> Kino.Input.read()
|> Day4Part1.solve()
23847

Part 2

defmodule Day4Part2 do
  def solve(input) do
    cards_won =
      String.split(input, "\n", trim: true)
      |> Enum.reduce(Map.new(), fn line, acc ->
        [card, numbers] = String.split(line, ": ", trim: true)
        [_, card_number] = String.split(card, " ", trim: true)
        card_number = String.to_integer(card_number)
        [winning | [have]] = String.split(numbers, " | ", trim: true)
        winning = String.split(winning, " ", trim: true) |> Enum.map(&amp;String.to_integer/1)
        have = String.split(have, " ", trim: true) |> Enum.map(&amp;String.to_integer/1)
        num_cards_won = find_score(winning, have)
        Map.put(acc, card_number, num_cards_won)
      end)

    all_counts =
      Enum.reduce(cards_won, Map.new(), fn {card_number, _}, acc ->
        find_total_count(card_number, cards_won, acc)
      end)

    Enum.reduce(all_counts, 0, fn {_, v}, acc -> v + acc end)
  end

  defp find_total_count(card_number, cards_won, memo) do
    if not Map.has_key?(cards_won, card_number) do
      memo
    else
      if Map.has_key?(memo, card_number) do
        memo
      else
        count = Map.get(cards_won, card_number)

        if count == 0 do
          Map.put(memo, card_number, 1)
        else
          memo =
            Enum.reduce((card_number + 1)..(card_number + count), memo, fn c_num, acc ->
              find_total_count(c_num, cards_won, acc)
            end)

          total_children_counts =
            (card_number + 1)..(card_number + count) |> Enum.map(&amp;Map.get(memo, &amp;1)) |> Enum.sum()

          # +1 because of itself.
          Map.put(memo, card_number, total_children_counts + 1)
        end
      end
    end
  end

  defp find_score(winning, have) do
    set =
      Enum.reduce(winning, MapSet.new(), fn x, acc ->
        MapSet.put(acc, x)
      end)

    winning_cards = Enum.filter(have, fn x -> MapSet.member?(set, x) end)
    length(winning_cards)
  end
end
{:module, Day4Part2, <<70, 79, 82, 49, 0, 0, 18, ...>>, {:find_score, 2}}
input
|> Kino.Input.read()
|> Day4Part2.solve()
8570000