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

Day 3

aoc-2024/day3.livemd

Day 3

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

Input

  input = Kino.Input.textarea("Puzzle Input")

Part 1

re = ~r/mul\((\d*,\d*)\)/

text = Kino.Input.read(input)

Regex.scan(re, text, capture: :all)
|> Enum.map(&Enum.at(&1, 1))
|> Enum.map(fn v ->
  v
  |> String.split(",")
  |> Enum.map(&String.to_integer/1)
  |> Enum.reduce(&*/2)
end)
|> Enum.sum()

Part 2 - Dos and Don’ts

  input = Kino.Input.textarea("Puzzle Input")

Decided to go fancy and try using Enum.chunk_while/4 function that allowed me to easily track what is the current mode.

re = ~r/mul\((\d*,\d*)\)|(do\(\))|(don't\(\))/

text = Kino.Input.read(input)

Regex.scan(re, text, capture: :all)
|> Enum.map(&Enum.at(&1, -1))
|> Enum.map(fn v ->
  case v do
    "do()" ->
      :do

    "don't()" ->
      :dont

    v ->
      v
      |> String.split(",")
      |> Enum.map(&String.to_integer/1)
      |> Enum.reduce(&*/2)
  end
end)
|> Enum.chunk_while(
  {:do, []},
  fn v, {action, acc} ->
    cond do
      # If the mode changes, just dump the current accumulator
      v == :dont or v == :do -> {:cont, acc, {v, []}}
      # Ignore values if we're in don't mode
      action == :dont -> {:cont, {:dont, acc}}
      # Otherwise add it to the chunk
      action == :do -> {:cont, {:do, [v | acc]}}
    end
  end,
  # Last chunk will be added depending on the mode that we hit last
  fn {action, last} = acc -> if action == :dont, do: {:cont, acc}, else: {:cont, last, acc} end
)
# sum chunks first
|> Enum.map(&Enum.sum/1)
|> Enum.sum()

Today I Learned

  • Enum.chunk_while/4 is fun
  • Regex.scan