Powered by AppSignal & Oban Pro

Advent of code 2025 - Day 8

day08.livemd

Advent of code 2025 - Day 8

Description

Day 8 - Playground

defmodule Load do
  def input do
    File.read!("#{__DIR__}/inputs/day08.txt")
  end
end
defmodule Day8 do
  def parse(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&String.split(&1, ",", trim: true))
    |> Enum.map(fn list ->
      [x, y, z] = Enum.map(list, &String.to_integer/1)
      {x, y, z}
    end)
  end

  def euclidean_distance({x1, y1, z1}, {x2, y2, z2}) do
    :math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2 + (z2 - z1) ** 2)
  end

  def generate_pairs(points) do
    for p1 <- points, p2 <- points, p1 < p2 do
      {euclidean_distance(p1, p2), p1, p2}
    end
    |> Enum.sort()
  end

  def part1(input, count) do
    points = parse(input)
    pairs = generate_pairs(points)
    initial_circuits = points |> Enum.map(fn point -> [point] end)

    circuits =
      pairs
      |> Enum.take(count)
      |> Enum.reduce(initial_circuits, fn {_dist, p1, p2}, circuits ->
        {circuit1, rest} = Enum.split_with(circuits, fn c -> p1 in c end)
        {circuit2, rest} = Enum.split_with(rest, fn c -> p2 in c end)
        merged = List.flatten(circuit1) ++ List.flatten(circuit2)
        [merged | rest]
      end)

    circuits
    |> Enum.map(&amp;length/1)
    |> Enum.sort()
    |> Enum.reverse()
    |> Enum.take(3)
    |> Enum.reduce(fn length, result -> length * result end)
  end

  def part2(input) do
    points = parse(input)
    pairs = generate_pairs(points)
    initial_circuits = points |> Enum.map(fn point -> [point] end)
    total_points = length(points)

    last_pair =
      pairs
      |> Enum.reduce_while(
        initial_circuits,
        fn {_dist, p1, p2}, circuits ->
          {circuit1, rest} = Enum.split_with(circuits, fn c -> p1 in c end)
          {circuit2, rest} = Enum.split_with(rest, fn c -> p2 in c end)
          merged = List.flatten(circuit1) ++ List.flatten(circuit2)
          new_circuits = [merged | rest]

          circuits
          |> Enum.map(&amp;length/1)

          [merged | rest]

          if length(merged) == total_points do
            {:halt, {p1, p2}}
          else
            {:cont, new_circuits}
          end
        end
      )

    {{x1, _, _}, {x2, _, _}} = last_pair
    x1 * x2
  end
end
ExUnit.start(autorun: false)

defmodule Test do
  use ExUnit.Case, async: true

  @input """
  162,817,812
  57,618,57
  906,360,560
  592,479,940
  352,342,300
  466,668,158
  542,29,236
  431,825,988
  739,650,466
  52,470,668
  216,146,977
  819,987,18
  117,168,530
  805,96,715
  346,949,466
  970,615,88
  941,993,340
  862,61,35
  984,92,344
  425,690,689
  """

  test "part 1" do
    assert Day8.part1(@input, 10) == 40
  end

  test "part 2" do
    assert Day8.part2(@input) == 25272
  end
end

ExUnit.run()
Day8.part1(Load.input(), 1000)
Day8.part2(Load.input())