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

Day 3

2024/day3.livemd

Day 3

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

Part 1

content =
  Kino.FS.file_path("day3_1.txt")
  |> File.read!()
  |> String.trim()
defmodule MulParser do
  def scan_mul(input) do
    Regex.scan(~r/mul\(\d{1,3}[,]\d{1,3}\)/, input)
    |> List.flatten()
    # For each mul expression -> Get the two integers
    |> Enum.map(fn line ->
      Regex.scan(~r/\d{1,3}/, line)
      |> List.flatten()
      |> Enum.map(fn n -> String.to_integer(n) end)
      # Multiply the integers
      |> Enum.reduce(fn x, acc -> x * acc end)
    end)
    # Sum for result
    |> Enum.sum()
  end
end

MulParser.scan_mul(content)

Part 2

# For this one we will use the same scan_mul in sections where it is enabled
defmodule Conditional do
  def split_at_first_occurrence(string, delimiter) do
    # Split the string at the delimiter, with only 2 parts so that it splits at first occurence
    parts = String.split(string, delimiter, parts: 2)

    # Return the parts as a tuple
    case parts do
      [head, tail] -> {head, tail}
      # If the delimiter is not found, return the original string and an empty string
      _ -> {string, ""}
    end
  end

  def compute(input, result, computing) do
    case computing do
      :do ->
        # Split head and tail
        {head, tail} = Conditional.split_at_first_occurrence(input, "don't()")

        # Compute result, if there is nothing left, return result
        if tail == "" do
          MulParser.scan_mul(head) + result
        else
          # Else compute the tail as well
          compute(tail, MulParser.scan_mul(head) + result, :dont)
        end

      :dont ->
        # On don't case we just ignore the head and goto tail
        {_head, tail} = Conditional.split_at_first_occurrence(input, "do()")

        if tail == "" do
          result
        else
          compute(tail, result, :do)
        end
    end
  end
end

Conditional.compute(content, 0, :do)