Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
defmodule Day20 do
def part1() do
{id, _particle} = Enum.min_by(particles(), &particle_weight/1)
id
end
def part2() do
particles = particles()
count_survivors(particles, sorted_collisions(particles))
end
defp particle_weight({_id, {p, v, a}}), do:
{weight(a), weight(v), weight(p)}
defp weight({x, y, z}), do: abs(x) + abs(y) + abs(z)
defp sorted_collisions(particles) do
indexed_particles = Map.new(particles)
last_particle = Map.size(indexed_particles) - 1
(for id1 <- 0..(last_particle - 1),
id2 <- (id1 + 1)..last_particle,
%{^id1 => particle1, ^id2 => particle2} = indexed_particles,
collisions = particle_collisions(particle1, particle2),
collisions != [],
do: Enum.map(collisions, &{&1, [id1, id2]}))
|> List.flatten()
|> Enum.group_by(fn {collision, _particles} -> collision end, fn {_collision, particles} -> particles end)
|> Enum.map(fn {collision, particles} -> {collision, MapSet.new(List.flatten(particles))} end)
|> Enum.sort()
end
defp count_survivors(particles, sorted_collisions) do
Enum.reduce(
sorted_collisions,
particles |> Enum.map(fn {id, _particle} -> id end) |> MapSet.new(),
fn {_collision, collided_ids}, survived_ids ->
alive_collided_ids = MapSet.intersection(collided_ids, survived_ids)
if MapSet.size(alive_collided_ids) > 1,
do: MapSet.difference(survived_ids, alive_collided_ids),
else: survived_ids
end
)
|> MapSet.size()
end
defp particle_collisions(
{{x1, y1, z1}, {vx1, vy1, vz1}, {ax1, ay1, az1}},
{{x2, y2, z2}, {vx2, vy2, vz2}, {ax2, ay2, az2}}
) do
(for collided_x <- valid_collision({x1, vx1, ax1}, {x2, vx2, ax2}),
collided_y <- valid_collision({y1, vy1, ay1}, {y2, vy2, ay2}),
collided_z <- valid_collision({z1, vz1, az1}, {z2, vz2, az2}),
same_moment_collision?(collided_x, collided_y) and same_moment_collision?(collided_x, collided_z),
do: [collided_x, collided_y, collided_z])
|> case do
[] -> []
collisions ->
collisions
|> List.flatten()
|> Stream.uniq()
|> Enum.reject(&(&1 == :all))
|> case do
[] -> [0]
collisions -> collisions
end
end
end
defp same_moment_collision?(:all, _), do: true
defp same_moment_collision?(_, :all), do: true
defp same_moment_collision?(x, x), do: true
defp same_moment_collision?(_, _), do: false
defp valid_collision({p, v, a}, {p, v, a}), do: [:all]
defp valid_collision({p1, v1, a1}, {p2, v2, a2}), do:
solve_square_equation(
(a2 - a1) / 2,
(a2 - a1) / 2 + (v2 - v1),
p2 - p1
)
|> Stream.filter(&(&1 > 0))
|> Stream.filter(&(trunc(&1) == &1))
|> Stream.map(&trunc/1)
defp solve_square_equation(0.0, 0.0, _), do: []
defp solve_square_equation(0.0, b, c), do: [-c/b]
defp solve_square_equation(a, b, c) do
case b * b - 4 * a * c do
n when n >= 0.0 ->
sqrt = :math.sqrt(n)
[(-b + sqrt) / (2 * a), (-b - sqrt) / (2 * a)]
_ -> []
end
|> Enum.uniq()
end
defp particles(), do:
"input.txt"
|> File.stream!()
|> Stream.map(&String.trim/1)
|> Stream.map(&decode_particle/1)
|> Stream.with_index()
|> Stream.map(fn {particle, id} -> {id, particle} end)
defp decode_particle(line), do:
line
|> String.split(", ")
|> Enum.map(&decode_vector/1)
|> List.to_tuple()
defp decode_vector(<<_::binary-size(2), rest::binary>>), do:
rest
|> String.replace(~r/<|>/,"")
|> String.split(",")
|> Enum.map(&String.to_integer/1)
|> List.to_tuple()
end
Day20.part1() |> IO.inspect
Day20.part2() |> IO.inspect
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment