Powered by AppSignal & Oban Pro

Advent of code 2024 - Day 7

aoc2024day7.livemd

Advent of code 2024 - Day 7

Mix.install([
  {:kino, "~> 0.14.2"}
])

Part 1

https://adventofcode.com/2024/day/7

textarea = Kino.Input.textarea("Give input:")
equations =
  Kino.Input.read(textarea)
  |> String.split("\n", trim: true)
  |> Enum.map(fn line -> [total, values_line] = String.split(line, ": ") 
        { String.to_integer(total), Enum.map(String.split(values_line), &String.to_integer/1) }
     end)

:ok
defmodule Solve do
  defp wrap_single_list([not_a_list | r]) when not is_list(not_a_list), do: [[not_a_list | r]]
  defp wrap_single_list(list_of_list), do: list_of_list

  defp equation(_total, [], _oper), do: []
  defp equation(total, [total], oper), do: Enum.reverse(oper)
  defp equation(_total, [_val], _oper), do: []

  defp equation(total, [val1, val2 | other], oper) do
    plus = equation(total, [val1 + val2 | other], ["+" | oper])
    multi = equation(total, [val1 * val2 | other], ["*" | oper])

    if plus != [] and multi != [] do
      # two solutions
      wrap_single_list(plus) ++ wrap_single_list(multi)
    else
      plus ++ multi
    end
  end

  def equation({total, values}), do: equation(total, values, [])
end

["*", "*"] = Solve.equation({45, [3, 5, 3]})
["+", "*"] = Solve.equation({24, [3, 5, 3]})
["*", "+"] = Solve.equation({18, [3, 5, 3]})
[] = Solve.equation({19, [3, 4, 5]})
[["+", "*"], ["*", "+"]] = Solve.equation({3267, [81, 40, 27]})

[["*", "+", "*", "*", "+"], ["*", "*", "+", "*", "+"]] =
  Solve.equation({6537, [1, 81, 40, 27, 2, 3]})

# [["*", "+", "*", "*", "+"], ["*", "*", "+", "*", "+"]] =
[["+", "*", "*", "*"], ["*", "*", "*", "*"]] = Solve.equation({48, [2, 2, 2, 3, 2]})

[
  ["+", "+", "*", "+", "+", "+", "*", "+", "*", "*", "*"],
  ["*", "*", "+", "+", "+", "*", "+", "+", "+", "*", "*"],
  ["*", "*", "+", "+", "+", "*", "+", "+", "*", "+", "*"]
] = Solve.equation({6174, [5, 4, 3, 11, 9, 9, 7, 4, 401, 1, 1, 6]})

Enum.filter(equations, fn equation -> Solve.equation(equation) != [] end)
|> Enum.map(fn {total, _values} -> total end)
|> Enum.sum()

Part 2

defmodule SolvePart2 do
  defp wrap_single_list([not_a_list | r]) when not is_list(not_a_list), do: [[not_a_list | r]]
  defp wrap_single_list(list_of_list), do: list_of_list
  
  defp equation(_total, [], _oper), do: []
  defp equation(total, [total], operation_list), do: Enum.reverse(operation_list)
  defp equation(_total, [_val], _oper), do: []

  # when negative numbers and 0 are not in the input, apply this optimalization:
  # defp equation(total, [val | _other], _oper) when val > total, do: []

  defp equation(total, [val1, val2 | other], oper) do
    plus = equation(total, [val1 + val2 | other], ["+" | oper])
    multi = equation(total, [val1 * val2 | other], ["*" | oper])

    concat =
      equation(
        total,
        [String.to_integer(to_string(val1) <> to_string(val2)) | other],
        ["|" | oper]
      )

    solutions = Enum.filter([plus, multi, concat], fn operation_list -> operation_list != [] end)
 
    if Enum.count(solutions) == 1 do
      [operation_list] = solutions
      operation_list
    else
      Enum.map(solutions, &amp;wrap_single_list/1) |> Enum.reduce([], fn x, acc -> x ++ acc end)
    end
  end

  def equation({total, values}), do: equation(total, values, [])
end

["|"] = SolvePart2.equation({156, [15, 6]})

[
  ["*", "*", "+", "+", "+", "*", "+", "+", "*", "+", "*"],
  ["*", "*", "+", "+", "+", "*", "+", "+", "+", "*", "*"],
  ["+", "+", "*", "+", "+", "+", "*", "+", "*", "*", "*"]
] = SolvePart2.equation({6174, [5, 4, 3, 11, 9, 9, 7, 4, 401, 1, 1, 6]})

Enum.filter(equations, fn equation -> SolvePart2.equation(equation) != [] end)
|> Enum.map(fn {total, _values} -> total end)
|> Enum.sum()