Day 4
Input
This time it is a little bit more convoluted, as there are 2 parts of the input. Fortunately we can easily disect the parts via pattern matching.
Technically the conversion to the numbers is not needed, but it does no harm and provides additional layer of safety against some whitespace characters left there and here.
The Day4.win/2 function is manually unrolled, as it is easier to write than some
random jumping in the list.
[numbers | bingos] =
File.read!("day4.txt")
|> String.split("\n\n", trim: true)
numbers =
numbers
|> String.trim()
|> String.split(",")
|> Enum.map(&String.to_integer/1)
bingos =
bingos
|> Enum.map(fn bingo ->
bingo
|> String.split(~r/\s+/, trim: true)
|> Enum.map(&String.to_integer/1)
end)
defmodule Day4 do
def win(
[
a1, a2, a3, a4, a5,
b1, b2, b3, b4, b5,
c1, c2, c3, c4, c5,
d1, d2, d3, d4, d5,
e1, e2, e3, e4, e5
],
nums
) do
# Rows
all_in([a1, a2, a3, a4, a5], nums) or
all_in([b1, b3, b3, b4, b5], nums) or
all_in([c1, c2, c3, c4, c5], nums) or
all_in([d1, d2, d3, d4, d5], nums) or
all_in([e1, e2, e3, e4, e5], nums) or
# Columns
all_in([a1, b1, c1, d1, e1], nums) or
all_in([a2, b2, c2, d2, e2], nums) or
all_in([a3, b3, c3, d3, e3], nums) or
all_in([a4, b4, c4, d4, e4], nums) or
all_in([a5, b5, c5, d5, e5], nums)
end
def not_matched(bingo, nums) do
Enum.reject(bingo, &(&1 in nums))
end
defp all_in(list, nums) do
Enum.all?(list, &(&1 in nums))
end
end
{:module, Day4, <<70, 79, 82, 49, 0, 0, 15, ...>>, {:all_in, 2}}
Task 1
We simply traverse the numbers list aggregating the numbers (order doesn’t really matter,
here we aggregate them in reverse order to speedup the code). When we have enough numbers
that any of the bingos is winning one, then we halt the reduction and return computed
result.
numbers
|> Enum.reduce_while([], fn elem, acc ->
matches = [elem | acc]
case Enum.find(bingos, &Day4.win(&1, matches)) do
nil -> {:cont, matches}
bingo -> {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
end
end)
34506
Task 2
numbers
|> Enum.reduce_while({bingos, []}, fn elem, {bingos, acc} ->
matches = [elem | acc]
case bingos do
[bingo] ->
if Day4.win(bingo, matches) do
{:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
else
{:cont, {bingos, matches}}
end
_ ->
{:cont, {Enum.reject(bingos, &Day4.win(&1, matches)), matches}}
end
end)
7686