Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

2024 Day7

advent_of_code/2024/day-07.livemd

2024 Day7

Day 7: Bridge Repair

Day 7: Bridge Repair

Part 1

以下の手順で問題を解決します。

  1. 入力を解析し、テスト値と数値リストを抽出します。
  2. 各方程式について、すべての演算子の組み合わせ(+ と *)を生成します。
  3. 左から順に評価し、テスト値と一致するか確認します。
  4. テスト値と一致する方程式のテスト値を合計します。
defmodule AdventOfCode2024Day7Part1 do
  def total_calibration_result(input) do
    input
    |> parse_input()
    |> Enum.filter(fn {target, numbers} ->
      valid_equation?(target, numbers)
    end)
    |> Enum.map(fn {target, _} -> target end)
    |> Enum.sum()
  end

  defp parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      [left, right] = String.split(line, ":")
      target = String.trim(left) |> String.to_integer()
      numbers = String.split(right) |> Enum.map(&String.to_integer/1)
      {target, numbers}
    end)
  end

  defp valid_equation?(target, numbers) do
    operators = [:+, :*]

    operator_permutations(length(numbers) - 1, operators)
    |> Enum.any?(fn ops ->
      evaluate_expression(numbers, ops) == target
    end)
  end

  defp operator_permutations(0, _operators), do: [[]]

  defp operator_permutations(n, operators) do
    for op <- operators,
        rest <- operator_permutations(n - 1, operators) do
      [op | rest]
    end
  end

  defp evaluate_expression([first | rest_numbers], operators) do
    Enum.zip(rest_numbers, operators)
    |> Enum.reduce(first, fn {num, op}, acc ->
      apply_operation(acc, op, num)
    end)
  end

  defp apply_operation(a, :+, b), do: a + b
  defp apply_operation(a, :*, b), do: a * b
end

# 使用例
input = """
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
"""

input |> AdventOfCode2024Day7Part1.total_calibration_result() |> IO.puts()

Part 2

Part 2では、新たに連結演算子 || が追加されます。連結演算子は左辺と右辺の数字を結合して1つの数値にします。例えば、12 || 34512345 になります。演算の評価順序は引き続き左から順です。

これにより、連結演算子を含めた3つの方程式が新たに可能になります。

手順

  1. 入力を解析して、テスト値と数値リストを抽出します。
  2. 各方程式について、すべての演算子の組み合わせ(+*||)を生成します。
  3. 演算子の組み合わせごとに、左から順に評価し、テスト値と一致するか確認します。
  4. テスト値と一致する方程式のテスト値を合計します。
defmodule AdventOfCode2024Day7Part2 do
  def total_calibration_result(input) do
    input
    |> parse_input()
    |> Enum.filter(fn {target, numbers} ->
      valid_equation?(target, numbers)
    end)
    |> Enum.map(fn {target, _} -> target end)
    |> Enum.sum()
  end

  defp parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      [left, right] = String.split(line, ":")
      target = String.trim(left) |> String.to_integer()
      numbers = String.split(right) |> Enum.map(&amp;String.to_integer/1)
      {target, numbers}
    end)
  end

  defp valid_equation?(target, numbers) do
    operators = [:+, :*, :||]

    operator_permutations(length(numbers) - 1, operators)
    |> Enum.any?(fn ops ->
      evaluate_expression(numbers, ops) == target
    end)
  end

  defp operator_permutations(0, _operators), do: [[]]

  defp operator_permutations(n, operators) do
    for op <- operators,
        rest <- operator_permutations(n - 1, operators) do
      [op | rest]
    end
  end

  defp evaluate_expression([first | rest_numbers], operators) do
    Enum.zip(rest_numbers, operators)
    |> Enum.reduce(first, fn {num, op}, acc ->
      apply_operation(acc, op, num)
    end)
  end

  defp apply_operation(a, :+, b), do: a + b
  defp apply_operation(a, :*, b), do: a * b
  defp apply_operation(a, :||, b) do
    String.to_integer("#{a}#{b}")
  end
end

# 使用例
input = """
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
"""

input |> AdventOfCode2024Day7Part2.total_calibration_result() |> IO.inspect()