Powered by AppSignal & Oban Pro

Day 8: Playground

advent_of_code/2025/day-08.livemd

Day 8: Playground

Section

Day 8: Playground

Day 8: Playground

Part 1

defmodule Awesome do
  def combination(_, 0), do: [[]]
  def combination([], _), do: []

  def combination([x | xs], n) do
    for(y <- combination(xs, n - 1), do: [x | y]) ++ combination(xs, n)
  end
end

defmodule AdventOfCode2025Day8Part1Solver do
  def run(list_of_lists, n) do
    sorted_list_of_lists = sort(list_of_lists)
    Enum.at(sorted_list_of_lists, -1)

    0..(n - 1)
    |> Enum.reduce([], fn i, acc ->
      [point1, point2] = Enum.at(sorted_list_of_lists, i)

      index1 = Enum.find_index(acc, fn set -> point1 in set end)
      index2 = Enum.find_index(acc, fn set -> point2 in set end)

      cond do
        index1 &amp;&amp; index2 &amp;&amp; index1 != index2 ->
          # 両方別グループにいる → 合体
          set1 = Enum.at(acc, index1)
          set2 = Enum.at(acc, index2)
          merged = MapSet.union(set1, set2)
          acc |> List.delete_at(max(index1, index2)) |> List.delete_at(min(index1, index2)) |> then(&amp;[merged | &amp;1])
  
        index1 ->
          List.update_at(acc, index1, &amp;MapSet.put(&amp;1, point2))
  
        index2 ->
          List.update_at(acc, index2, &amp;MapSet.put(&amp;1, point1))
  
        true ->
          [MapSet.new([point1, point2]) | acc]
      end
    end)
    |> Enum.sort_by(&amp;Enum.count/1, :desc)
    |> Enum.take(3)
    |> Enum.map(&amp;Enum.count/1)
    |> Enum.product()
  end

  defp sort(list_of_lists) do
    list_of_lists
    |> Awesome.combination(2)
    |> Enum.sort_by(fn points ->
      Enum.zip_with(points, fn [a, b] -> (a - b) ** 2 end)
      |> Enum.sum()
    end)
  end
end

defmodule AdventOfCode2025Day8Part1 do
  def run(input, n \\ 1000) do
    input
    |> parse_input()
    |> solve(n)
  end

  defp solve(list_of_lists, n) do
    AdventOfCode2025Day8Part1Solver.run(list_of_lists, n)
  end

  defp parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      line
      |> String.split(",", trim: true)
      |> Enum.map(&amp;String.to_integer/1)
    end)
  end
end
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
"""

AdventOfCode2025Day8Part1.run(input, 10)

Part 2

defmodule AdventOfCode2025Day8Part2Solver do
  def run(list_of_lists) do
    acc = Enum.map(list_of_lists, &amp; MapSet.new([&amp;1]))

    list_of_lists
    |> sort()
    |> Enum.reduce_while({acc, nil}, fn [point1, point2], {acc, nil} ->

      index1 = Enum.find_index(acc, fn set -> point1 in set end)
      index2 = Enum.find_index(acc, fn set -> point2 in set end)

      cond do
        index1 &amp;&amp; index2 &amp;&amp; index1 != index2 ->
          # 両方別グループにいる → 合体
          set1 = Enum.at(acc, index1)
          set2 = Enum.at(acc, index2)
          merged = MapSet.union(set1, set2)
          new_acc = acc |> List.delete_at(max(index1, index2)) |> List.delete_at(min(index1, index2)) |> then(&amp;[merged | &amp;1])

          if Enum.count(new_acc) == 1 do
            [x1, _, _] = point1
            [x2, _, _] = point2
            {:halt, {new_acc, x1 * x2}}
          else
            {:cont, {new_acc, nil}}
          end

        index1 ->
          {:cont, {List.update_at(acc, index1, &amp;MapSet.put(&amp;1, point2)), nil}}
  
        index2 ->
          {:cont, {List.update_at(acc, index2, &amp;MapSet.put(&amp;1, point1)), nil}}
      end
    end)
    |> elem(1)
  end

  defp sort(list_of_lists) do
    list_of_lists
    |> Awesome.combination(2)
    |> Enum.sort_by(fn points ->
      Enum.zip_with(points, fn [a, b] -> (a - b) ** 2 end)
      |> Enum.sum()
    end)
  end
end

defmodule AdventOfCode2025Day8Part2 do
  def run(input) do
    input
    |> parse_input()
    |> solve()
  end

  defp solve(list_of_lists) do
    AdventOfCode2025Day8Part2Solver.run(list_of_lists)
  end

  defp parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      line
      |> String.split(",", trim: true)
      |> Enum.map(&amp;String.to_integer/1)
    end)
  end
end
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
"""

AdventOfCode2025Day8Part2.run(input)