Day 4
Mix.install([:kino])
Get input
input =
Kino.Input.textarea("Paste input here",
default: """
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
"""
)
cards =
input
|> Kino.Input.read()
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
[_, card, winners, numbers] =
Regex.run(~r/Card\s+(\d+):((?:\s+\d+)+)\s+?\|((?:\s+\d+)+)/, line)
{String.to_integer(card), String.split(winners, " ", trim: true),
String.split(numbers, " ", trim: true)}
end)
defmodule Helper do
def count_winners(winners, numbers) do
MapSet.intersection(
MapSet.new(winners),
MapSet.new(numbers)
)
|> MapSet.size()
end
end
Part 1
cards
|> Stream.map(fn {_, winners, numbers} ->
Helper.count_winners(winners, numbers)
end)
|> Stream.filter(&Kernel.>(&1, 0))
|> Stream.map(fn match_count -> 2 ** (match_count - 1) end)
|> Enum.sum()
Part 2
cards
|> Stream.map(fn {card, winners, numbers} ->
{card, Helper.count_winners(winners, numbers)}
end)
# for each card in the stack, get which copies of other cards it wins and increment
# the win count for each of them in the `count` lookup Map. We also have to remember to
# increment the win count for the current card too.
|> Enum.reduce(%{}, fn
{n, 0}, counts ->
Map.update(counts, n, 1, &(&1 + 1))
{n, wins}, counts ->
Enum.reduce(1..wins, Map.update(counts, n, 1, &(&1 + 1)), fn j, cc ->
inc = Map.get(cc, n)
Map.update(cc, n + j, inc, &(&1 + inc))
end)
end)
|> Map.values()
|> Enum.sum()