-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathday_04.ex
98 lines (82 loc) · 2.83 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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
defmodule AdventOfCode.Y2021.Day04 do
@moduledoc """
--- Day 4: Giant Squid ---
Problem Link: https://adventofcode.com/2021/day/4
Difficulty: s
Tags: grid random-access optimization
"""
alias AdventOfCode.Helpers.{InputReader, Transformers}
def input, do: InputReader.read_from_file(2021, 4)
def run(input \\ input()) do
input = parse(input)
{play_1(input), play_2(input)}
end
def parse(data) do
[order_data | board_data] = String.split(data, ~r{(\r\n\r\n|\r\r|\n\n)}, trim: true)
{get_orders(order_data), get_boards(board_data)}
end
defp get_orders(data), do: data |> String.split(",") |> Enum.map(&String.to_integer/1)
defp get_boards(data), do: data |> Enum.with_index() |> Enum.map(&get_board/1)
defp get_board({data, idx}) do
data
|> Transformers.lines()
|> Enum.flat_map(fn line ->
line
|> String.split(" ", trim: true)
|> Enum.map(&String.to_integer/1)
end)
|> Enum.with_index()
|> Enum.map(fn {key, idx} -> {key, {div(idx, 5), rem(idx, 5)}} end)
|> Enum.into(%{})
|> then(&board_state(&1, idx))
end
defp board_state(board, idx), do: {idx, board, [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]}
def play_1({orders, board_states}) do
orders
|> Enum.reduce_while(board_states, fn order, acc ->
acc = Enum.map(acc, &mark_board(&1, order))
case get_winner(acc) do
nil -> {:cont, acc}
board -> {:halt, {score(board), order}}
end
end)
|> Tuple.product()
end
def score(board), do: board |> Map.keys() |> Enum.sum()
defp mark_board({idx, board, rows, cols} = state, order) do
case Map.pop(board, order) do
{{row, col}, board} ->
{idx, board, List.update_at(rows, row, &(&1 + 1)), List.update_at(cols, col, &(&1 + 1))}
{nil, _} ->
state
end
end
defp get_winner(board_states) do
Enum.reduce_while(board_states, nil, fn {_, board, rows, cols}, _ ->
(5 in rows or (5 in cols && {:halt, board})) || {:cont, nil}
end)
end
def play_2({orders, board_states}) do
orders
|> Enum.reduce({board_states, []}, fn order, {acc, winners} ->
acc = Enum.map(acc, &mark_board(&1, order))
{acc,
[Enum.map(get_winners(acc), fn {idx, board} -> {idx, score(board), order} end) | winners]}
end)
|> elem(1)
|> Enum.reverse()
|> Enum.drop_while(&Enum.empty?/1)
|> Enum.flat_map(&Function.identity/1)
|> last_winner(Enum.count(board_states), MapSet.new())
|> Tuple.product()
end
defp last_winner([{idx, score, order} | rest], size, map_set) do
set = MapSet.put(map_set, idx)
(Enum.count(set) == size && {score, order}) || last_winner(rest, size, set)
end
defp get_winners(board_states) do
Enum.reduce(board_states, [], fn {idx, board, rows, cols}, acc ->
((5 in rows or 5 in cols) && [{idx, board} | acc]) || acc
end)
end
end