Advent of Code 2024 - Day 07
Mix.install([
{:kino_aoc, "~> 0.1.7"}
])
Introduction
2024 - Day 07
Puzzle
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2024", "7", System.fetch_env!("LB_AOC_SESSION"))
Parser
Code - Parser
defmodule Parser do
def parse(input) do
input
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
[sum, num] =
line
|> String.split(":")
[
String.to_integer(sum),
num |> String.split(" ", trim: true) |> Enum.map(&String.to_integer/1)
]
end)
end
end
Tests - Parser
ExUnit.start(autorun: false)
defmodule ParserTest do
use ExUnit.Case, async: true
import Parser
@input """
10: 1 10
12: 2 2
"""
@expected [[10, [1, 10]], [12, [2, 2]]]
test "parse test" do
actual = parse(@input)
assert actual == @expected
end
end
ExUnit.run()
Shared
defmodule Shared do
def run(input, build_op) do
input
|> Parser.parse()
|> Enum.map(fn [sum, num = [head | tail]] ->
Task.async(fn ->
(Enum.count(num) - 1)
|> build_op.()
|> Enum.any?(fn op ->
tail
|> Enum.reduce({head, op}, fn n, {result, [op | ops]} ->
{f(op).(result, n), ops}
end)
|> elem(0)
|> then(&(&1 == sum))
end)
|> then(fn
true -> sum
_ -> 0
end)
end)
end)
|> Enum.map(&(Task.await(&1, :infinity)))
|> Enum.sum()
end
def f("+"), do: &+/2
def f("*"), do: &*/2
def f("m"), do: &merge/2
defp merge(a, b) do
b
|> Integer.digits()
|> Enum.count()
|> then(&Integer.pow(10, &1))
|> Kernel.*(a)
|> Kernel.+(b)
end
end
Part One
Code - Part 1
defmodule PartOne do
def solve(input) do
IO.puts("--- Part One ---")
IO.puts("Result: #{run(input)}")
end
def run(input), do: Shared.run(input, &build_op/1)
defp build_op(1), do: [["+"], ["*"]]
defp build_op(length) do
1..(length - 1)
|> Enum.reduce(["+", "*"], fn _, ops ->
for op1 <- ops, op2 <- ["+", "*"], do: op1 <> op2
end)
|> Enum.map(&String.codepoints/1)
end
end
Tests - Part 1
ExUnit.start(autorun: false)
defmodule PartOneTest do
use ExUnit.Case, async: true
import PartOne
@input """
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
"""
@expected 3749
test "part one" do
actual = run(@input)
assert actual == @expected
end
end
ExUnit.run()
Solution - Part 1
PartOne.solve(puzzle_input)
Part Two
Code - Part 2
defmodule PartTwo do
def solve(input) do
IO.puts("--- Part Two ---")
IO.puts("Result: #{run(input)}")
end
def run(input), do: Shared.run(input, &build_op/1)
def build_op(1), do: [["+"], ["*"], ["m"]]
def build_op(length) do
1..(length - 1)
|> Enum.reduce(["+", "*", "m"], fn _, ops ->
for op1 <- ops, op2 <- ["+", "*", "m"], do: op1 <> op2
end)
|> Enum.map(&String.codepoints/1)
end
end
Tests - Part 2
ExUnit.start(autorun: false)
defmodule PartTwoTest do
use ExUnit.Case, async: true
import PartTwo
@input """
190: 10 19
3267: 81 40 27
83: 17 5
156: 15 6
7290: 6 8 6 15
161011: 16 10 13
192: 17 8 14
21037: 9 7 18 13
292: 11 6 16 20
"""
@expected 11387
test "part two" do
actual = run(@input)
assert actual == @expected
end
end
ExUnit.run()
Solution - Part 2
PartTwo.solve(puzzle_input)