This repository has been archived by the owner on Jan 6, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
day_04.ex
75 lines (61 loc) · 2.46 KB
/
day_04.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
defmodule AdventOfCode.Day04 do
import AdventOfCode.Utils
@type board :: [[integer]]
@spec part1([binary()]) :: integer()
def part1(args) do
{sequence, boards} = parse_args(args)
{winning_board, numbers} = find_winning_board([], sequence, boards)
(winning_board |> unmarked_numbers(numbers) |> Enum.sum()) * hd(numbers)
end
# Find the winning 5x5 bingo board for a given sequence of numbers
@spec find_winning_board([integer], [integer], [board]) :: {board, [integer]}
defp find_winning_board(numbers, sequence, boards) do
case boards |> Enum.find(&is_winning_board?(&1, numbers)) do
nil ->
find_winning_board([hd(sequence) | numbers], tl(sequence), boards)
board ->
{board, numbers}
end
end
@spec part2([binary()]) :: integer()
def part2(args) do
{sequence, boards} = parse_args(args)
{losing_board, numbers} = find_losing_board([], sequence, boards)
(losing_board |> unmarked_numbers(numbers) |> Enum.sum()) * hd(numbers)
end
# Find the least-likely-to-win 5x5 bingo board for a given sequence of numbers
@spec find_losing_board([integer], [integer], [board]) :: {board, [integer]}
defp find_losing_board(numbers, sequence, boards) do
case Enum.filter(boards, &(!is_winning_board?(&1, numbers))) do
[] ->
{hd(boards), numbers}
filtered_boards ->
find_losing_board([hd(sequence) | numbers], tl(sequence), filtered_boards)
end
end
@spec is_winning_board?(board, [integer]) :: boolean
defp is_winning_board?(board, numbers) do
rows = board |> Enum.chunk_every(5)
columns = rows |> Enum.zip() |> Enum.map(&Tuple.to_list/1)
Enum.any?(rows ++ columns, fn row_or_col ->
Enum.all?(row_or_col, &Enum.member?(numbers, &1))
end)
end
@spec unmarked_numbers(board, [integer]) :: [integer]
defp unmarked_numbers(board, numbers) do
Enum.filter(board, &(!Enum.member?(numbers, &1)))
end
@spec parse_args([binary()]) :: {[integer()], [board()]}
defp parse_args(args) do
[[raw_sequence] | raw_boards] =
Enum.map(args, &String.trim/1)
|> Enum.chunk_by(&(&1 == ""))
|> Enum.filter(&(&1 !== [""]))
sequence = String.split(raw_sequence, ",") |> Enum.map(&parse_int!/1)
boards = raw_boards |> Enum.map(&parse_board_numbers/1)
{sequence, boards}
end
@spec parse_board_numbers([String.t()]) :: [integer]
defp parse_board_numbers(rows),
do: Enum.join(rows, " ") |> String.split() |> Enum.map(&parse_int!/1)
end