Advent of Code - Day 7
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Problem
https://adventofcode.com/2024/day/7
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2024", "7", System.fetch_env!("LB_AOC_SESSION"))
example_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"
defmodule Day7 do
def parse(str) do
str
|> String.split("\n", trim: true)
|> Enum.map(fn eq ->
[x, nums] = String.split(eq, ":", limit: 2, trim: true)
parsed_nums = String.split(nums, " ", trim: true) |> Enum.map(&String.to_integer(&1))
{String.to_integer(x), parsed_nums}
end)
end
def op(nil, b, _), do: b
def op(a, b, "0"), do: a + b
def op(a, b, "1"), do: a * b
def op(a, b, "2"), do: String.to_integer("#{a}#{b}")
def solvable?({goal, nums}, base) do
len = length(nums) - 1
max = String.to_integer(String.duplicate("#{base-1}", len), base)
nums_with_index = nums |> Enum.with_index()
Enum.any?(0..max, fn i ->
op_combo = String.pad_leading(Integer.to_string(i, base), len, "0") |> String.graphemes()
goal == Enum.reduce_while(nums_with_index, nil, fn {num, idx}, acc ->
if acc == nil || acc <= goal do
{:cont, op(acc, num, Enum.at(op_combo, idx - 1)) }
else
{:halt, nil }
end
end)
end)
end
def async_solve(problem, base) do
Task.async_stream(problem, fn {goal, nums} ->
if solvable?({goal, nums}, base) do
goal
else
0
end
end)
|> Enum.to_list()
|> Enum.reduce(0, fn {:ok, x}, acc -> acc + x end)
end
def solve_part1(str) do
parse(str) |> async_solve(2)
end
def solve_part2(str) do
parse(str) |> async_solve(3)
end
end
Day7.solve_part1(example_input)
Day7.solve_part1(puzzle_input)
Day7.solve_part2(example_input)
Day7.solve_part2(puzzle_input)