this repo has no description
1<!-- vim:set ft=markdown: -->
2
3<!-- livebook:{"persist_outputs":true} -->
4
5# Day 4
6
7## Input
8
9This time it is a little bit more convoluted, as there are 2 parts of the input.
10Fortunately we can easily disect the parts via pattern matching.
11
12Technically the conversion to the numbers is not needed, but it does no harm
13and provides additional layer of safety against some whitespace characters left there
14and here.
15
16The `Day4.win/2` function is manually unrolled, as it is easier to write than some
17random jumping in the list.
18
19<!-- livebook:{"disable_formatting":true} -->
20
21```elixir
22[numbers | bingos] =
23 File.read!("day4.txt")
24 |> String.split("\n\n", trim: true)
25
26numbers =
27 numbers
28 |> String.trim()
29 |> String.split(",")
30 |> Enum.map(&String.to_integer/1)
31
32bingos =
33 bingos
34 |> Enum.map(fn bingo ->
35 bingo
36 |> String.split(~r/\s+/, trim: true)
37 |> Enum.map(&String.to_integer/1)
38 end)
39
40defmodule Day4 do
41 def win(
42 [
43 a1, a2, a3, a4, a5,
44 b1, b2, b3, b4, b5,
45 c1, c2, c3, c4, c5,
46 d1, d2, d3, d4, d5,
47 e1, e2, e3, e4, e5
48 ],
49 nums
50 ) do
51 # Rows
52 all_in([a1, a2, a3, a4, a5], nums) or
53 all_in([b1, b3, b3, b4, b5], nums) or
54 all_in([c1, c2, c3, c4, c5], nums) or
55 all_in([d1, d2, d3, d4, d5], nums) or
56 all_in([e1, e2, e3, e4, e5], nums) or
57 # Columns
58 all_in([a1, b1, c1, d1, e1], nums) or
59 all_in([a2, b2, c2, d2, e2], nums) or
60 all_in([a3, b3, c3, d3, e3], nums) or
61 all_in([a4, b4, c4, d4, e4], nums) or
62 all_in([a5, b5, c5, d5, e5], nums)
63 end
64
65 def not_matched(bingo, nums) do
66 Enum.reject(bingo, &(&1 in nums))
67 end
68
69 defp all_in(list, nums) do
70 Enum.all?(list, &(&1 in nums))
71 end
72end
73```
74
75```output
76{:module, Day4, <<70, 79, 82, 49, 0, 0, 15, ...>>, {:all_in, 2}}
77```
78
79## Task 1
80
81We simply traverse the `numbers` list aggregating the numbers (order doesn't really matter,
82here we aggregate them in reverse order to speedup the code). When we have enough numbers
83that any of the `bingos` is winning one, then we halt the reduction and return computed
84result.
85
86```elixir
87numbers
88|> Enum.reduce_while([], fn elem, acc ->
89 matches = [elem | acc]
90
91 case Enum.find(bingos, &Day4.win(&1, matches)) do
92 nil -> {:cont, matches}
93 bingo -> {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
94 end
95end)
96```
97
98```output
9934506
100```
101
102## Task 2
103
104```elixir
105numbers
106|> Enum.reduce_while({bingos, []}, fn elem, {bingos, acc} ->
107 matches = [elem | acc]
108
109 case bingos do
110 [bingo] ->
111 if Day4.win(bingo, matches) do
112 {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
113 else
114 {:cont, {bingos, matches}}
115 end
116
117 _ ->
118 {:cont, {Enum.reject(bingos, &Day4.win(&1, matches)), matches}}
119 end
120end)
121```
122
123```output
1247686
125```