-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday13.ex
145 lines (119 loc) · 3.4 KB
/
day13.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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
defmodule Day13 do
def part1(input) do
input
|> parse_input()
|> Enum.chunk_every(2)
|> find_right_order_pair_index()
|> Enum.sum()
|> IO.inspect(label: "part1")
end
def part2(input) do
divider_packets = [[[2]], [[6]]]
input
|> parse_input()
|> Enum.concat(divider_packets)
|> Enum.sort(&right_order?/2)
|> calculate_decoder_key(divider_packets)
|> IO.inspect(label: "part2")
end
defp parse_input(input) do
input
|> String.split("\n", trim: true)
|> Enum.map(&parse_packet/1)
end
defp parse_packet(packet) do
packet
|> String.graphemes()
|> Enum.map(fn ele -> if ele in ["[", "]", ","], do: ele, else: String.to_integer(ele) end)
|> Enum.reduce({[], nil}, fn ele, {result, cur} ->
case ele do
"," ->
result = if cur == nil, do: result, else: put_nested_list(result, cur)
{result, nil}
"[" ->
result = put_nested_list(result, [])
{result, nil}
"]" ->
result =
if cur == nil do
put_nested_list(result, nil)
else
result |> put_nested_list(cur) |> put_nested_list(nil)
end
{result, nil}
num ->
cur = if cur == nil, do: num, else: cur * 10 + num
{result, cur}
end
end)
|> elem(0)
|> remove_nil()
|> hd()
|> reverse_nested_list()
end
defp put_nested_list([], cur), do: [cur]
defp put_nested_list(list, cur) do
[ele | remain] = list
cond do
is_list(ele) and not ended_list?(ele) -> [put_nested_list(ele, cur) | remain]
is_list(ele) -> [cur | list]
true -> [cur | list]
end
end
defp ended_list?([nil | _]), do: true
defp ended_list?(_list), do: false
defp remove_nil(list) do
list
|> Enum.reject(&is_nil/1)
|> Enum.map(&if is_list(&1), do: remove_nil(&1), else: &1)
end
defp reverse_nested_list(list) do
list
|> Enum.reverse()
|> Enum.map(&if is_list(&1), do: reverse_nested_list(&1), else: &1)
end
defp find_right_order_pair_index(pairs) do
pairs
|> Enum.with_index(1)
|> Enum.filter(fn {[left, right], _index} -> right_order?(left, right) end)
|> Enum.map(&elem(&1, 1))
end
defp right_order?([], []), do: :cont
defp right_order?([], _right), do: true
defp right_order?(_left, []), do: false
defp right_order?(left, right) do
[l_val | l_remain] = left
[r_val | r_remain] = right
cond do
is_integer(l_val) and is_integer(r_val) ->
if l_val == r_val,
do: right_order?(l_remain, r_remain),
else: l_val < r_val
is_integer(l_val) ->
case right_order?([l_val], r_val) do
:cont -> right_order?(l_remain, r_remain)
result -> result
end
is_integer(r_val) ->
case right_order?(l_val, [r_val]) do
:cont -> right_order?(l_remain, r_remain)
result -> result
end
true ->
case right_order?(l_val, r_val) do
:cont -> right_order?(l_remain, r_remain)
result -> result
end
end
end
defp calculate_decoder_key(packets, divider_packets) do
packets
|> Enum.with_index(1)
|> Enum.filter(fn {packet, _index} -> Enum.any?(divider_packets, &(&1 == packet)) end)
|> Enum.map(&elem(&1, 1))
|> Enum.product()
end
end
File.read!("input.txt")
|> tap(&Day13.part1/1)
|> tap(&Day13.part2/1)