···
1
-
<!-- vim: syntax=markdown -->
2
-
<!-- livebook:{"persist_outputs":true} -->
3
+
livebook:{"persist_outputs":true}
···
···
|> Stream.map(&String.to_integer(String.trim(&1)))
29
-
line_or_bytes: :line,
30
-
modes: [:raw, :read_ahead, :binary],
34
-
funs: [#Function<47.58486609/1 in Stream.map/2>]
···
|> Enum.count(fn [a, b] -> a < b end)
<!-- livebook:{"break_markdown":true} -->
···
|> Enum.count(fn [a, _, _, b] -> a < b end)
···
116
-
enum: %File.Stream{
117
-
line_or_bytes: :line,
118
-
modes: [:raw, :read_ahead, :binary],
122
-
funs: [#Function<47.58486609/1 in Stream.map/2>]
···
···
···
187
-
{:module, Day3, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:count, 1}}
···
···
198
+
This time it is a little bit more convoluted, as there are 2 parts of the input.
199
+
Fortunately we can easily disect the parts via pattern matching.
201
+
Technically the conversion to the numbers is not needed, but it does no harm
202
+
and provides additional layer of safety against some whitespace characters left there
205
+
The `Day4.win/2` function is manually unrolled, as it is easier to write than some
206
+
random jumping in the list.
208
+
<!-- livebook:{"disable_formatting":true} -->
211
+
[numbers | bingos] =
212
+
File.read!("day4.txt")
213
+
|> String.split("\n\n", trim: true)
218
+
|> String.split(",")
219
+
|> Enum.map(&String.to_integer/1)
223
+
|> Enum.map(fn bingo ->
225
+
|> String.split(~r/\s+/, trim: true)
226
+
|> Enum.map(&String.to_integer/1)
232
+
a1, a2, a3, a4, a5,
233
+
b1, b2, b3, b4, b5,
234
+
c1, c2, c3, c4, c5,
235
+
d1, d2, d3, d4, d5,
241
+
all_in([a1, a2, a3, a4, a5], nums) or
242
+
all_in([b1, b3, b3, b4, b5], nums) or
243
+
all_in([c1, c2, c3, c4, c5], nums) or
244
+
all_in([d1, d2, d3, d4, d5], nums) or
245
+
all_in([e1, e2, e3, e4, e5], nums) or
247
+
all_in([a1, b1, c1, d1, e1], nums) or
248
+
all_in([a2, b2, c2, d2, e2], nums) or
249
+
all_in([a3, b3, c3, d3, e3], nums) or
250
+
all_in([a4, b4, c4, d4, e4], nums) or
251
+
all_in([a5, b5, c5, d5, e5], nums) or
253
+
all_in([a1, b2, c3, d4, e5], nums) or
254
+
all_in([a5, b4, c3, d2, e1], nums)
257
+
def not_matched(bingo, nums) do
258
+
Enum.reject(bingo, &(&1 in nums))
261
+
defp all_in(list, nums) do
262
+
Enum.all?(list, &(&1 in nums))
269
+
We simply traverse the `numbers` list aggregating the numbers (order doesn't really matter,
270
+
here we aggregate them in reverse order to speedup the code). When we have enough numbers
271
+
that any of the `bingos` is winning one, then we halt the reduction and return computed
276
+
|> Enum.reduce_while([], fn elem, acc ->
277
+
matches = [elem | acc]
279
+
case Enum.find(bingos, &Day4.win(&1, matches)) do
280
+
nil -> {:cont, matches}
281
+
bingo -> {:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
290
+
|> Enum.reduce_while({bingos, []}, fn elem, {bingos, acc} ->
291
+
matches = [elem | acc]
295
+
if Day4.win(bingo, matches) do
296
+
{:halt, Enum.sum(Day4.not_matched(bingo, matches)) * elem}
298
+
{:cont, {bingos, matches}}
302
+
{:cont, {Enum.reject(bingos, &Day4.win(&1, matches)), matches}}