|
| 1 | +defmodule Day24 do |
| 2 | + def read_input(file_path) do |
| 3 | + file_path |> File.read!() |> String.split("\n", trim: true) |> Enum.map(&parse_line/1) |
| 4 | + end |
| 5 | + |
| 6 | + def parse_line(line) do |
| 7 | + [px, py, pz, vx, vy, vz] = |
| 8 | + line |> String.split(~r/\ |,|@/, trim: true) |> Enum.map(&String.to_integer/1) |
| 9 | + |
| 10 | + m = vy / vx |
| 11 | + b = py - m * px |
| 12 | + %{vel: {vx, vy, vz}, pos: {px, py, pz}, m: m, b: b} |
| 13 | + end |
| 14 | + |
| 15 | + def valid_intersection_xy( |
| 16 | + %{ |
| 17 | + pos: {pxa, _pya, _pza}, |
| 18 | + vel: {vxa, _vya, _vza}, |
| 19 | + m: ma, |
| 20 | + b: ba |
| 21 | + }, |
| 22 | + %{ |
| 23 | + pos: {pxb, _pyb, _pzb}, |
| 24 | + vel: {vxb, _vyb, _vzb}, |
| 25 | + m: mb, |
| 26 | + b: bb |
| 27 | + } |
| 28 | + ) do |
| 29 | + if abs(ma - mb) < 1.0e-8 do |
| 30 | + false |
| 31 | + else |
| 32 | + x = (bb - ba) / (ma - mb) |
| 33 | + y = ma * x + ba |
| 34 | + |
| 35 | + ta = (x - pxa) / vxa |
| 36 | + tb = (x - pxb) / vxb |
| 37 | + |
| 38 | + ta >= 0 and tb >= 0 and x >= 200_000_000_000_000 and x <= 400_000_000_000_000 and |
| 39 | + y >= 200_000_000_000_000 and y <= 400_000_000_000_000 |
| 40 | + end |
| 41 | + end |
| 42 | + |
| 43 | + def count_intersections_xy(list), do: count_intersections_xy(list, 0) |
| 44 | + def count_intersections_xy([], acc), do: acc |
| 45 | + |
| 46 | + def count_intersections_xy([x | xs], acc) do |
| 47 | + count_intersections_xy(xs, acc + (xs |> Enum.count(&valid_intersection_xy(&1, x)))) |
| 48 | + end |
| 49 | + |
| 50 | + def dot({a1, a2, a3}, {b1, b2, b3}), do: a1 * b1 + a2 * b2 + a3 * b3 |
| 51 | + |
| 52 | + def cross({a1, a2, a3}, {b1, b2, b3}), |
| 53 | + do: { |
| 54 | + a2 * b3 - b2 * a3, |
| 55 | + a3 * b1 - b3 * a1, |
| 56 | + a1 * b2 - b1 * a2 |
| 57 | + } |
| 58 | + |
| 59 | + def neg({a, b, c}), do: {-a, -b, -c} |
| 60 | + def add({a1, a2, a3}, {b1, b2, b3}), do: {a1 + b1, a2 + b2, a3 + b3} |
| 61 | + def sub(a, b), do: add(a, neg(b)) |
| 62 | + def mult(m, {x, y, z}), do: {m * x, m * y, m * z} |
| 63 | + |
| 64 | + def plane(%{pos: p1, vel: v1}, %{pos: p2, vel: v2}) do |
| 65 | + p12 = sub(p1, p2) |
| 66 | + v12 = sub(v1, v2) |
| 67 | + vv = cross(v1, v2) |
| 68 | + {cross(p12, v12), dot(p12, vv)} |
| 69 | + end |
| 70 | + |
| 71 | + def rock(%{pos: p1, vel: v1} = h1, %{pos: p2, vel: v2} = h2, %{pos: _p3, vel: _v3} = h3) do |
| 72 | + {a, a_} = plane(h1, h2) |
| 73 | + {b, b_} = plane(h1, h3) |
| 74 | + {c, c_} = plane(h2, h3) |
| 75 | + w = mult(a_, cross(b, c)) |> add(mult(b_, cross(c, a))) |> add(mult(c_, cross(a, b))) |
| 76 | + t = dot(a, cross(b, c)) |
| 77 | + w = mult(1 / t, w) |
| 78 | + w1 = sub(v1, w) |
| 79 | + w2 = sub(v2, w) |
| 80 | + ww = cross(w1, w2) |
| 81 | + e = dot(ww, cross(p2, w2)) |
| 82 | + f = dot(ww, cross(p1, w1)) |
| 83 | + g = dot(p1, ww) |
| 84 | + s = dot(ww, ww) |
| 85 | + mult(e / s, w1) |> add(mult(-f / s, w2)) |> add(mult(g / s, ww)) |
| 86 | + end |
| 87 | + |
| 88 | + def partA(file_path) do |
| 89 | + # Somehow, partA works for my real input but fails on sample input, interesting. |
| 90 | + file_path |> read_input() |> count_intersections_xy() |
| 91 | + end |
| 92 | + |
| 93 | + def partB(file_path) do |
| 94 | + [a, b, c | _] = file_path |> read_input() |
| 95 | + {x, y, z} = rock(a, b, c) |
| 96 | + round(x + y + z) |
| 97 | + end |
| 98 | +end |
| 99 | + |
| 100 | +IO.puts(Day24.partA("./input")) |
| 101 | +IO.puts(Day24.partB("./input")) |
0 commit comments