Hello all, hopefully I post this before someone else does and I don’t dupe.
IMO Day 4 was much easier than Day 3 (yay, I can sleep before work!)
My set-up:
defmodule Card do
defp to_list_numbers(str) do
str
|> String.split(" ")
|> Enum.filter(fn s -> String.length(s) !== 0 end)
|> Enum.map(fn u ->
{i, _} = Integer.parse(u)
i
end)
end
def parse("Card " <> rest) do
[_, numbers] = rest |> String.split(":")
[winning, player] = numbers |> String.split("|")
[winning |> String.trim() |> to_list_numbers(), player |> String.trim() |> to_list_numbers()]
end
end
cards = for card <- input |> String.split("\n"),
[winning_numbers, our_numbers] = Card.parse(card) do
[winning_numbers, our_numbers]
end
Then part 1 was really simple (10 minutes from start for me to get here):
for [winning_numbers, our_numbers] <- cards
do
case count_winning_cards.(our_numbers, winning_numbers) do
n when n > 0 -> 2**(n-1)
0 -> 0
end
end |> Enum.sum()
Part 2 slightly less so (40 minutes from start to get here):
cards
|> Enum.with_index()
|> Enum.reduce(
List.duplicate(1, cards |> Enum.count()),
fn {[winning, player], idx}, copies ->
self_copies = Enum.at(copies, idx)
won = count_winning_cards.(player, winning)
slice = if won > 0, do: (idx+1)..(idx+won), else: ..
copies |> Enum.with_index() |> Enum.map(
fn {count, idx} ->
if idx in slice, do: count + self_copies, else: count
end
)
end
) |> Enum.sum()
The biggest time sink for me was remembering that 1..1
is a non-empty range in Elixir, so I needed to write the slice
to be:
slice = if won > 0, do: (idx+1)..(idx+won), else: ..