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

Day 3 - Advent of Code 2024

advent-of-code/2024/day-3.livemd

Day 3 - Advent of Code 2024

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

Setup

input = Kino.Input.textarea("Paste in your input:", default: "xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))")

Part 1

defmodule Computer.Parsers do
  import NimbleParsec

  def mul_instruction do
    ignore(string("mul("))
    |> integer(min: 1, max: 3)
    |> ignore(string(","))
    |> integer(min: 1, max: 3)
    |> ignore(string(")"))
    |> tag(:mul)
  end

  def irrelevant_char do
    ignore(utf8_char([]))
  end
end

defmodule Computer do
  import NimbleParsec
  import Computer.Parsers

  defparsec :parse_instructions, repeat(choice([mul_instruction(), irrelevant_char()]))

  def execute(program) do
    program
    |> parse_instructions()
    |> elem(1)
    |> Stream.map(fn {:mul, [x, y]} -> x * y end)
    |> Enum.sum()
  end
end
program = Kino.Input.read(input)
Computer.execute(program)

Part 2

defmodule Computer.V2 do
  import NimbleParsec
  import Computer.Parsers

  do_instruction =
    ignore(string("do()"))
    |> tag(:do)

  dont_instruction =
    ignore(string("don't()"))
    |> tag(:dont)

  choices =
    choice([
      do_instruction,
      dont_instruction,
      mul_instruction(),
      irrelevant_char()
    ])

  defparsec :parse_instructions, repeat(choices)

  def execute(program) do
    program
    |> parse_instructions()
    |> elem(1)
    |> Enum.reduce({true, 0}, fn instruction, {enabled, total} ->
      with {:mul, [x, y]} <- instruction,
           true <- enabled do
        {true, total + x * y}
      else
        {:do, _} -> {true, total}
        {:dont, _} -> {false, total}
        _ -> {enabled, total}
      end
    end)
    |> elem(1)
  end
end
Computer.V2.execute(program)