this repo has no description
1<!-- vim:ft=markdown -->
2
3<!-- livebook:{"persist_outputs":true} -->
4
5# Day 19
6
7## Section
8
9```elixir
10# File.read!("day19.txt")
11input =
12 File.read!("day19.txt")
13 |> String.split("\n\n")
14 |> Enum.map(fn scanner ->
15 scanner
16 |> String.split("\n", trim: true)
17 |> Enum.drop(1)
18 |> Enum.map(fn coords ->
19 coords
20 |> String.split(",")
21 |> Enum.map(&String.to_integer/1)
22 |> Enum.zip(~w[x y z]a)
23 |> Map.new(fn {v, k} -> {k, v} end)
24 end)
25 |> MapSet.new()
26 end)
27
28length(input)
29```
30
31<!-- livebook:{"output":true} -->
32
33```
3434
35```
36
37$$
38\left\{
39\begin{alignat*}{3}
40b_{x_1} r_x + b_{x_0} = a_{x_1} \\
41b_{x_2} r_x + b_{x_0} = a_{x_2}
42\end{alignat*}
43\right.
44\left\{
45\begin{alignat*}{3}
46b_{y_1} r_y + b_{y_0} = a_{y_1} \\
47b_{y_2} r_y + b_{y_0} = a_{y_2} \\
48\end{alignat*}
49\right.
50\left\{
51\begin{alignat*}{3}
52b_{z_1} r_z + b_{z_0} = a_{z_1} \\
53b_{z_2} r_z + b_{z_0} = a_{z_2} \\
54\end{alignat*}
55\right.
56$$
57
58Where $$r_x, r_y, r_z \in \{-1, 1\}$$ and $$a_{xyz}, b_{xyz} = \text{const}$$.
59This mean that we need to solve:
60
61$$
62B_x x + b_0 = a_x \\
63B_y y + b_0 = a_y \\
64B_z z + b_0 = a_z
65$$
66
67Where:
68
69$$
70B_d = \begin{bmatrix}
71{b_1}_d & 1 \\
72{b_2}_d & 1 \\
73\end{bmatrix}
74d = \begin{bmatrix}
75r_d \\
76b_{d_0}
77\end{bmatrix}
78a_d = \begin{bmatrix}
79{a_1}_d \\
80{a_2}_d
81\end{bmatrix}
82$$
83
84By applying [Cramer's Rule](https://en.wikipedia.org/wiki/Cramer%27s_rule) we can solve these
85linear equations with:
86
87$$
88e_d = \frac{
89\begin{vmatrix}
90{b_1}_d & 1 \\
91{b_2}_d & 1
92\end{vmatrix}
93}{
94\begin{vmatrix}
95{a_1}_d & 1 \\
96{a_2}_d & 1
97\end{vmatrix}
98} = \frac{{b_1}_d - {b_2}_d}{{a_1}_d - {a_2}_d} \\
99b_{d_0} = \frac{
100 \begin{vmatrix}
101{a_1}_d & {b_1}_d \\
102{a_2}_d & {b_2}_d
103\end{vmatrix}
104}{
105 \begin{vmatrix}
106{a_1}_d & 1 \\
107{a_2}_d & 1
108\end{vmatrix}
109} = \frac{{a_1}_d {b_2}_d - {b_1}_d {a_2}_d}{{a_1}_d - {a_2}_d}
110$$
111
112```elixir
113defmodule Day19 do
114 def rebase(base, pairs1) do
115 pairs0 = pairs(base)
116 # Take common points
117 pa = Map.take(pairs0, Map.keys(pairs1))
118 pb = Map.take(pairs1, Map.keys(pairs0))
119
120 # Merge them
121 [{{a, b, axes1}, {_, _, axes2}} = p0 | others] =
122 Map.merge(pa, pb, fn _, a, b -> {a, b} end)
123 |> Map.values()
124
125 p1 = Enum.find(others, fn {{x, y, _}, _} -> a in [x, y] end)
126 p2 = Enum.find(others, fn {{x, y, _}, _} -> b in [x, y] end)
127
128 axes = axes_transformations(axes1, axes2)
129
130 a_1 = a
131 a_2 = b
132 b_1 = reaxe(select_common(p0, p1), axes)
133 b_2 = reaxe(select_common(p0, p2), axes)
134
135 transform =
136 for i <- ~w[x y z]a, into: %{} do
137 a_1 = a_1[i]
138 a_2 = a_2[i]
139 b_1 = b_1[i]
140 b_2 = b_2[i]
141 det_b = b_1 - b_2
142 r = div(a_1 - a_2, det_b)
143
144 b_0 = div(b_1 * a_2 - a_1 * b_2, det_b)
145
146 {i, {r, b_0}}
147 end
148
149 new_points =
150 pairs1
151 |> Map.values()
152 |> Enum.flat_map(&[elem(&1, 0), elem(&1, 1)])
153 |> Enum.uniq()
154 |> Enum.map(&reaxe(&1, axes))
155 |> do_rebase(transform)
156 |> MapSet.new()
157
158 %{x: {_, sx}, y: {_, sy}, z: {_, sz}} = transform
159 scanner = %{x: sx, y: sy, z: sz}
160
161 {new_points, scanner}
162 end
163
164 defp do_rebase(points, transform) do
165 points
166 |> Enum.map(fn p ->
167 Map.merge(p, transform, fn _k, d, {r, b} -> d * r + b end)
168 end)
169 |> MapSet.new()
170 end
171
172 defp select_common(a, b) do
173 case {a, b} do
174 {{_, {_, x, _}}, {_, {_, x, _}}} -> x
175 {{_, {x, _, _}}, {_, {x, _, _}}} -> x
176 {{_, {_, x, _}}, {_, {x, _, _}}} -> x
177 {{_, {x, _, _}}, {_, {_, x, _}}} -> x
178 end
179 end
180
181 defp axes_transformations(a, b) do
182 Map.merge(a, b, fn
183 _, a, b -> {a, b}
184 end)
185 |> Map.values()
186 |> Map.new()
187 end
188
189 defp reaxe(point, axes) do
190 %{
191 x: point[axes[:x]],
192 y: point[axes[:y]],
193 z: point[axes[:z]]
194 }
195 end
196
197 def pairs(points) do
198 for a <- points,
199 b <- points,
200 a < b,
201 dx = abs(a.x - b.x),
202 dy = abs(a.y - b.y),
203 dz = abs(a.z - b.z),
204 into: %{},
205 do: {{dx ** 2 + dy ** 2 + dz ** 2, dx + dy + dz}, {a, b, %{dx => :x, dy => :y, dz => :z}}}
206 end
207end
208```
209
210<!-- livebook:{"output":true} -->
211
212```
213{:module, Day19, <<70, 79, 82, 49, 0, 0, 26, ...>>, {:pairs, 1}}
214```
215
216```elixir
217[first | rest] = Enum.map(input, &Day19.pairs/1)
218
219{joinable, later} =
220 Enum.split_with(rest, fn pairs ->
221 s1 = MapSet.new(first, fn {k, _} -> k end)
222 s2 = MapSet.new(pairs, fn {k, _} -> k end)
223
224 case MapSet.size(MapSet.intersection(s1, s2)) do
225 66 -> true
226 x when x < 66 -> false
227 end
228 end)
229
230{length(joinable), length(later)}
231```
232
233<!-- livebook:{"output":true} -->
234
235```
236{3, 30}
237```
238
239```elixir
240# Distances between each pair of points in `first`
241
242points_set = MapSet.new(hd(input))
243
244{points_set, scanners} =
245 Enum.reduce_while(
246 1..34,
247 {points_set, joinable, later, []},
248 fn i, {set, joinable, later, scanners} ->
249 {time, {set, scanners}} =
250 :timer.tc(fn ->
251 Enum.reduce(joinable, {set, scanners}, fn new, {base, scanners} ->
252 {new, scanner} = Day19.rebase(base, new)
253
254 {MapSet.union(new, base), [scanner | scanners]}
255 end)
256 end)
257
258 IO.inspect(time / 1_000_000)
259
260 current = Day19.pairs(set)
261
262 Enum.split_with(later, fn pairs ->
263 s1 = MapSet.new(current, fn {k, _} -> k end)
264 s2 = MapSet.new(pairs, fn {k, _} -> k end)
265
266 MapSet.size(MapSet.intersection(s1, s2)) >= 66
267 end)
268 |> case do
269 {[], []} ->
270 {:halt, {set, scanners}}
271
272 {joinable, later} ->
273 IO.inspect({length(joinable), length(later)}, label: i)
274 {:cont, {set, joinable, later, scanners}}
275 end
276 end
277 )
278
279MapSet.size(points_set)
280```
281
282<!-- livebook:{"output":true} -->
283
284```
2850.002822
2861: {5, 25}
2870.017052
2882: {5, 20}
2890.0355
2903: {7, 13}
2910.134655
2924: {6, 7}
2930.229952
2945: {5, 2}
2950.288351
2966: {1, 1}
2970.076665
2987: {1, 0}
2990.071942
300```
301
302<!-- livebook:{"output":true} -->
303
304```
305396
306```
307
308```elixir
309for(
310 a <- scanners,
311 b <- scanners,
312 do: abs(a.x - b.x) + abs(a.y - b.y) + abs(a.z - b.z)
313)
314|> Enum.max()
315```
316
317<!-- livebook:{"output":true} -->
318
319```
32011828
321```