# 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. ```elixir [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 ``` ```output {: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. ```elixir 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) ``` ```output 34506 ``` ## Task 2 ```elixir 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) ``` ```output 7686 ```