Advent of code 2025 day 8
Mix.install([
{:kino, "~> 0.18"}
])
Part 1
https://adventofcode.com/2025/day/8
input = Kino.Input.textarea("Please give me input:")
points =
Kino.Input.read(input)
|> String.split("\n", trim: true)
|> Enum.map(fn xyz ->
[x, y, z] =
String.split(xyz, ",")
|> Enum.map(&String.to_integer/1)
{x, y, z}
end)
length(points)
defmodule StringsOfLight do
defp calc_distances(points) do
# Make a distance table, requires (N^2 - N) / 2 calculations
# Double loop, where inner loop p-inner <> p-outer
# To prevent too much double calculations, p-inner < p-outer
# output: [{distance1, p1, p2}, ...]
for p1 = {x1, y1, z1} <- points,
p2 = {x2, y2, z2} <- points,
p1 < p2 do
dx = x2 - x1
dy = y2 - y1
dz = z2 - z1
distance = dx * dx + dy * dy + dz * dz
{distance, p1, p2}
end
|> List.keysort(0)
end
defp init_circuits(points) do
# fill single boxes
Enum.reduce(points, {0, %{}, %{}}, fn p, {circuit_next_val, circuits, boxes_in_circuits} ->
c_new =
circuits
|> Map.put(circuit_next_val, [p])
box_new =
boxes_in_circuits
|> Map.put(p, circuit_next_val)
{circuit_next_val + 1, c_new, box_new}
end)
end
defp calc_final_circuits_or_end_points(points, distances, stop_at_circuit_total) do
{_, init_circuits, init_boxes_in_circuits} = init_circuits(points)
Enum.reduce_while(distances, {init_circuits, init_boxes_in_circuits, nil}, fn {_distance, p1,
p2},
{circuits,
boxes_in_circuits,
_} ->
p1_circuit_index = Map.get(boxes_in_circuits, p1)
p2_circuit_index = Map.get(boxes_in_circuits, p2)
if p1_circuit_index == p2_circuit_index do
{:cont, {circuits, boxes_in_circuits, nil}}
else
boxes1 = Map.get(circuits, p1_circuit_index)
boxes2 = Map.get(circuits, p2_circuit_index)
c_new =
Map.put(circuits, p1_circuit_index, boxes1 ++ boxes2)
|> Map.delete(p2_circuit_index)
box_new =
Enum.reduce(boxes2, boxes_in_circuits, fn box, change_boxes_in_circuits ->
Map.put(change_boxes_in_circuits, box, p1_circuit_index)
end)
if stop_at_circuit_total >= 1 and Enum.count(Map.keys(c_new)) == stop_at_circuit_total do
{:halt, {c_new, box_new, {p1, p2}}}
else
{:cont, {c_new, box_new, nil}}
end
end
end)
end
def calc_final_circuits(points, distances) do
{final_circuits, _, _} = calc_final_circuits_or_end_points(points, distances, -1)
final_circuits
end
def calc_last_points(points, all_distances) do
{_circuits, _, last_points} =
calc_final_circuits_or_end_points(points, all_distances, 1)
last_points
end
def part1(points, nr_of_shortest_connects) do
distances =
calc_distances(points)
|> Enum.take(nr_of_shortest_connects)
calc_final_circuits(points, distances)
|> Enum.map(&elem(&1, 1))
|> Enum.sort_by(&length/1, :desc)
|> Enum.take(3)
|> Enum.product_by(&length/1)
end
def part2(points) do
distances =
calc_distances(points)
{{x1, _y1, _z1}, {x2, _y2, _z2}} = calc_last_points(points, distances)
x1 * x2
end
end
IO.inspect StringsOfLight.part1(points, 1000), label: "part1"
IO.inspect StringsOfLight.part2(points), label: "part2"