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

Day 4

2023/04.livemd

Day 4

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

Input

example = """
Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
"""

input = Kino.Input.textarea("Input", default: example)

Part 1

expected = [8, 2, 2, 1, 0, 0]
[8, 2, 2, 1, 0, 0]
defmodule Part1 do
  def parse(input) do
    for line <- String.split(input, "\n", trim: true) do
      [_, rest] = String.split(line, ": ")

      for numbers <- String.split(rest, "|") do
        for number <- String.split(numbers, " ", trim: true), into: MapSet.new() do
          String.to_integer(number)
        end
      end
    end
  end

  def run(input) do
    for [winnings, haves] <- parse(input) do
      case MapSet.intersection(winnings, haves) |> Enum.count() do
        0 -> 0
        n -> 2 ** (n - 1)
      end
    end
  end
end

Part1.run(example) == expected
true
Kino.Input.read(input)
|> Part1.run()
|> Enum.sum()
21558

Part 2

expected = [1, 2, 4, 8, 14, 1]
[1, 2, 4, 8, 14, 1]
defmodule Part2 do
  def inc(acc, i), do: Map.update(acc, i, 1, &amp;(&amp;1 + 1))

  def run(input) do
    Part1.parse(input)
    |> Enum.map(fn [winnings, haves] -> Enum.count(MapSet.intersection(winnings, haves)) end)
    |> Enum.with_index(1)
    |> Enum.reduce(%{}, fn
      {0, i}, acc ->
        inc(acc, i)

      {count, i}, acc ->
        acc = inc(acc, i)

        Enum.to_list((i + 1)..(i + count))
        |> List.duplicate(Map.get(acc, i))
        |> List.flatten()
        |> Enum.reduce(acc, &amp;inc(&amp;2, &amp;1))
    end)
    |> Map.values()
  end
end

Part2.run(example)
[1, 2, 4, 8, 14, 1]
Kino.Input.read(input)
|> Part2.run()
|> Enum.sum()
10425665