This was much easier than yesterday!
And I’m always proud of myself when I manage to write recursive code (mostly because I don’t need recursive code very often in my daily job)
Part1
defmodule Advent.Y2024.Day07.Part1 do
def run(puzzle) do
puzzle
|> parse()
|> Enum.filter(fn {result, nums} -> eval(result, nums) end)
|> Enum.map(&elem(&1, 0))
|> Enum.sum()
end
def parse(puzzle) do
puzzle
|> String.split("\n")
|> Enum.map(fn equation ->
[result, numbers] = String.split(equation, ": ")
numbers = numbers |> String.split(" ") |> Enum.map(&String.to_integer/1)
{String.to_integer(result), numbers}
end)
end
def eval(result, numbers, total \\ nil)
def eval(result, [], total), do: total == result
def eval(result, [number | tail], nil), do: eval(result, tail, number)
def eval(result, _numbers, total) when total > result, do: false
def eval(result, [number | tail], total) do
eval(result, tail, total + number) or eval(result, tail, total * number)
end
end
Part2
defmodule Advent.Y2024.Day07.Part2 do
alias Advent.Y2024.Day07.Part1
def run(puzzle) do
puzzle
|> Part1.parse()
|> Task.async_stream(fn {result, nums} -> {eval(result, nums), result} end)
|> Stream.map(fn {:ok, output} -> output end)
|> Stream.filter(fn {eval, _result} -> eval end)
|> Stream.map(fn {_eval, result} -> result end)
|> Enum.sum()
end
def eval(result, numbers, acc \\ nil)
def eval(result, [], acc), do: acc == result
def eval(result, [number | tail], nil), do: eval(result, tail, number)
def eval(result, _numbers, acc) when acc > result, do: false
def eval(result, [number | tail], acc) do
eval(result, tail, acc + number) or
eval(result, tail, acc * number) or
eval(result, tail, concat(acc, number))
end
defp concat(a, b) when b < 10, do: a * 10 + b
defp concat(a, b) when b < 100, do: a * 100 + b
defp concat(a, b) when b < 1000, do: a * 1000 + b
end
It takes about 10ms for part1 and 40ms for part2
edit: using Task.async_stream
and part 2 is now running in 20ms