My version would incorrectly accept 4 digit operands in the mul
directives.
Specification clearly said “X
and Y
are each 1-3 digit numbers”.
I just relied on the input being not malformed in that regard
My version would incorrectly accept 4 digit operands in the mul
directives.
Specification clearly said “X
and Y
are each 1-3 digit numbers”.
I just relied on the input being not malformed in that regard
I used NimbleParsec to build a parser:
defmodule Parser do
import NimbleParsec
disable =
ignore(string("don't()"))
|> tag(:disable)
enable =
ignore(string("do()"))
|> tag(:enable)
operand = integer(max: 3, min: 1)
mul =
ignore(string("mul("))
|> concat(operand)
|> ignore(string(","))
|> concat(operand)
|> ignore(string(")"))
|> tag(:mul)
instruction = choice([disable, enable, mul])
instructions =
eventually(instruction)
|> repeat()
defparsec(:parse, instructions |> eventually(eos()))
end
Today I learned Regex.split
with include_captures: true
option
Part1
defmodule Advent.Y2024.Day03.Part1 do
def run(puzzle) do
Regex.scan(~r|mul\(\d+,\d+\)|, puzzle)
|> Enum.map(fn [s] -> evaluate_mul(s) end)
|> Enum.sum()
end
def evaluate_mul(s) do
[_, n1, n2, _] = String.split(s, ["(", ",", ")"])
String.to_integer(n1) * String.to_integer(n2)
end
end
Part2
defmodule Advent.Y2024.Day03.Part2 do
alias Advent.Y2024.Day03.Part1
def run(puzzle) do
Regex.split(~r/mul\(\d+,\d+\)|(do\(\))|(don't\(\))/, puzzle, include_captures: true)
|> Enum.reduce(%{sum: 0, enabled: true}, fn
"do()", acc ->
%{acc | enabled: true}
"don't()", acc ->
%{acc | enabled: false}
s, acc = %{enabled: true, sum: sum} ->
if String.match?(s, ~r|mul\(\d+,\d+\)|) do
%{acc | sum: sum + Part1.evaluate_mul(s)}
else
acc
end
_, acc ->
acc
end)
|> Map.get(:sum)
end
end
I made codepoints and used extensive pattern matching instead of regex.
defp find_muls([], solution), do: solution
defp find_muls(["m", "u", "l", "(", a1, ",", b1, ")" | rest], solution) do
case {Integer.parse(a1), Integer.parse(b1)} do
{{n1, _}, {n2, _}} -> find_muls(rest, solution + n1 * n2)
_ -> find_muls(rest, solution)
end
end
defp find_muls(["m", "u", "l", "(", a1, ",", b1, b2, ")" | rest], solution) do
case {Integer.parse(a1), Integer.parse(b1 <> b2)} do
{{n1, _}, {n2, _}} -> find_muls(rest, solution + n1 * n2)
_ -> find_muls(rest, solution)
end
end
...
Posting just a part of it, it’s too big.
For part 2 I added a factor argument that goes to 1 when do is pattern matched, goes to 0 when don’t. Needless to say, I’m not satisfied with my solution.
I once wrote a lexer using Regex.split
with include_captures: true
and parts: 2, trim: false
.
Part1 i wont care about sharing. But Part2., i tried to scan over this thread and couldnt find something similar, but the solution i found was a fold left and pattern matching on functions and a tuple accumulator
actually day3 part2 was pretty cool task <3
really liked your part2 impl <3
Took some time to fix my initial code; but really wanted to make it within the cutoff limit of the code box at this forum ;). 24 lines, with official formatting that is.
defmodule Aoc2024.Solutions.Y24.Day03 do
alias AoC.Input
def parse(input, _part), do: Input.read!(input)
def part_one(problem), do: problem |> sanitize(~w(mul)) |> exec_calc() |> elem(1)
def part_two(problem), do: problem |> sanitize(~w(mul do don't)) |> exec_calc() |> elem(1)
defp sanitize(input, keywords),
do:
("(" <> Enum.join(keywords, "|") <> ")\\((?:([0-9]{1,3}),([0-9]{1,3}))?\\)")
|> Regex.compile!()
|> Regex.scan(input, capture: :all_but_first)
defp exec_calc(input),
do:
Enum.reduce(input, {:active, 0}, fn
["mul", x, y], {:active, sum} -> {:active, sum + String.to_integer(x) * String.to_integer(y)}
["do"], {_, sum} -> {:active, sum}
["don't"], {_, sum} -> {:inactive, sum}
_, acc -> acc
end)
end
Ok. At least part1 was way more easy for me than yesterday. Still working on part2
defmodule MyApp.Solutions.Y24.Day03 do
alias AoC.Input
def parse(input, _part) do
garbage = Input.read!(input)
Regex.scan(~r/mul\((\d+),(\d+)\)/, garbage)
end
def part_one(problem) do
problem
|> List.foldl(0, fn([_, x, y], acc) ->
acc + String.to_integer(x) * String.to_integer(y)
end)
end
i love how compact it is <3
Can definitely be tidied up and simplified, but still exploring different ways to solve a problem in Elixir
defmodule Aoc2024.Day3 do
def part_1(input) do
~r/mul\((\d{1,3}),(\d{1,3})\)/
|> Regex.scan(input, capture: :all_but_first)
|> Enum.reduce(0, fn [a, b], acc -> acc + (String.to_integer(a) * String.to_integer(b)) end)
end
def part_2(input) do
~r/mul\((\d{1,3}),(\d{1,3})\)|don't\(\)|do\(\)/
|> Regex.scan(input)
|> Enum.reduce({:enabled, 0}, fn row, acc ->
case process(row) do
:enabled ->
{:enabled, elem(acc, 1)}
:disabled ->
{:disabled, elem(acc, 1)}
x ->
case elem(acc, 0) do
:disabled ->
{:disabled, elem(acc, 1)}
:enabled ->
{:enabled, x + elem(acc, 1)}
end
end
end)
end
def process(row) when is_list(row) and length(row) == 1 do
case row do
["don't()"] ->
:disabled
["do()"] ->
:enabled
end
end
def process(row) when is_list(row) and length(row) == 3 do
[_ | rest] = row
rest
|> Enum.map(&String.to_integer/1)
|> Enum.product()
end
end
#!/usr/bin/env elixir
defmodule Day3 do
def run(test_file) do
list = File.read!(test_file)
IO.puts("Total Product Sum: #{total_product_sum(list)}")
IO.puts("Total Processed Sum: #{total_processed_sum(list)}")
end
def total_product_sum(list) do
regex = ~r/mul\((\d+),(\d+)\)/
list
|> extract_phrases(regex)
|> Stream.map(&extract_numbers_product/1)
|> Stream.map(fn {a, b} -> a * b end)
|> Enum.sum()
end
def total_processed_sum(list) do
regex = ~r/(do\(\)|don't\(\)|mul\((\d+),(\d+)\))/
list
|> extract_phrases(regex)
|> process_phrases()
|> Stream.map(fn {a, b} -> a * b end)
|> Enum.sum()
end
defp extract_phrases(content, regex), do: Regex.scan(regex, content)
defp extract_numbers_product([_, a, b]), do: {String.to_integer(a), String.to_integer(b)}
defp process_phrases(phrases) do
Enum.reduce(phrases, {true, []}, fn
# Enable mul instructions
["do()", _], {_, acc} ->
{true, acc}
# Disable mul instructions
["don't()", _], {_, acc} ->
{false, acc}
# Handle mul(a,b) when mul is enabled
["mul(" <> _, _, a, b], {true, acc} when a != nil and b != nil ->
{true, [{String.to_integer(a), String.to_integer(b)} | acc]}
# Handle mul(a,b) when mul is disabled (ignore)
["mul(" <> _, _, _, _], {false, acc} ->
{false, acc}
_, acc ->
acc
end)
|> elem(1)
end
end
Day3.run("./testdata.txt")
I have been naughty and just ran eval string on every mul(x,y) I collected
First time posting here I see. What an entry!
Approach: regex + code eval:
defmodule Aoc2024.Day03 do
def part1(file) do
Regex.scan(~r/mul\(\d+,\d+\)/, File.read!(file))
|> Enum.reduce(0, fn [code], sum -> sum + eval(code) end)
end
def part2(file) do
Regex.scan(~r/don't\(\)|do\(\)|mul\(\d+,\d+\)/, File.read!(file))
|> Enum.reduce({0, true}, fn
["do" <> rest], {sum, _} -> {sum, rest == "()"}
[code], {sum, add?} -> {sum + ((add? && eval(code)) || 0), add?}
end)
|> then(&elem(&1, 0))
end
def eval(code), do: elem(Code.eval_string("Aoc2024.Day03." <> code, [], __ENV__), 0)
def mul(a, b), do: a * b
end
Yeah, I always refactor those to (f.ex. for a tri-tuple):
|> then(fn {x, _y, _z} -> x end)
It’s just clearer and easier to read.
Ha yeah I actually did the opposite here. I was actually refactoring |> then(fn {sum, _} -> sum end)
(which I also prefer for readability) to be shorter. Only after I posted did I see what I’d done.
Wanted to try out something new and used nimble_parsec for the first time.