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

Advent of Code - 2024 - Day 7

advent-of-code/2024/day07.livemd

Advent of Code - 2024 - Day 7

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

Puzzle Input

puzzle_input = Kino.Input.textarea("Please paste the puzzle input:")

Part 1

defmodule Mordin do
  def cartesian_product(values, n) do
    cartesian_product(values, n, _products = [[]])
  end

  def cartesian_product(_values, 0, products), do: products

  def cartesian_product(values, n, products) do
    new_products = for product <- products, value <- values, do: [value | product]
    cartesian_product(values, n - 1, new_products)
  end

  def operator_combinations(values) do
    cartesian_product([&amp;+/2, &amp;*/2], Enum.count(values) - 1)
  end
end
defmodule Garrus do
  def read_calibrations(string) do
    string
    |> String.split("\n")
    |> Enum.map(fn line ->
      [total, values] = String.split(line, ": ")
      total = String.to_integer(total)
      values = values |> String.split() |> Enum.map(&amp;String.to_integer/1)
      operator_combinations = Mordin.operator_combinations(values)

      possible =
        operator_combinations
        |> Enum.any?(fn operators ->
          check_sequence(total, values, operators)
        end)

      if possible do
        total
      else
        0
      end
    end)
  end

  def check_sequence(total, values, operators) do
    result =
      values
      |> Enum.with_index()
      |> Enum.reduce([], fn {value, index}, acc ->
        [Enum.at(operators, index) | [value | acc]]
      end)
      |> List.flatten()
      |> Enum.reject(&amp;is_nil/1)
      |> Enum.reverse()
      |> calculate_sequence()

    result == total
  end

  def calculate_sequence([initial | terms]) do
    terms
    |> Enum.chunk_every(2)
    |> Enum.reduce(initial, fn [op, b], acc ->
      op.(acc, b)
    end)
  end
end
part_1_test_input = Kino.Input.textarea("Please paste the test input for part 1:")
part_1_test_input
|> Kino.Input.read()
|> Garrus.read_calibrations()
|> Enum.sum()
puzzle_input
|> Kino.Input.read()
|> Garrus.read_calibrations()
|> Enum.sum()

Part 2

defmodule Tali do
  def cartesian_product(values, n) do
    cartesian_product(values, n, _products = [[]])
  end

  def cartesian_product(_values, 0, products), do: products

  def cartesian_product(values, n, products) do
    new_products = for product <- products, value <- values, do: [value | product]
    cartesian_product(values, n - 1, new_products)
  end

  def operator_combinations(values) do
    cartesian_product([:+, :*, :||], Enum.count(values) - 1)
  end
end
defmodule Liara do
  def read_calibrations(string) do
    string
    |> String.split("\n")
    |> Enum.map(fn line ->
      [total, values] = String.split(line, ": ")
      total = String.to_integer(total)
      values = values |> String.split() |> Enum.map(&amp;String.to_integer/1)
      operator_combinations = Tali.operator_combinations(values)

      possible =
        operator_combinations
        |> Enum.any?(fn operators ->
          check_sequence(total, values, operators)
        end)

      if possible do
        total
      else
        0
      end
    end)
  end

  def check_sequence(total, values, operators) do
    result =
      values
      |> Enum.with_index()
      |> Enum.reduce([], fn {value, index}, acc ->
        [Enum.at(operators, index) | [value | acc]]
      end)
      |> List.flatten()
      |> Enum.reject(&amp;is_nil/1)
      |> Enum.reverse()
      |> calculate_sequence()

    result == total
  end

  def calculate_sequence([initial | terms]) do
    terms
    |> Enum.chunk_every(2)
    |> Enum.reduce(initial, fn [op, b], acc ->
      case op do
        :+ ->
          acc + b

        :* ->
          acc * b

        :|| ->
          Integer.to_string(acc) <> Integer.to_string(b) |> String.to_integer()
      end
    end)
  end
end
part_2_test_input = Kino.Input.textarea("Please paste the test input for part 2:")
part_2_test_input
|> Kino.Input.read()
|> Liara.read_calibrations()
|> Enum.sum()
puzzle_input
|> Kino.Input.read()
|> Liara.read_calibrations()
|> Enum.sum()