AoC20/lib/day9/day9.ex

115 lines
2.4 KiB
Elixir

defmodule Day9 do
@example """
35
20
15
25
47
40
62
55
65
95
102
117
150
182
127
219
299
277
309
576
"""
@example_premable_length 5
def part1(example \\ false) do
input = if example, do: @example, else: File.read!("lib/day9/input.txt")
length = if example, do: @example_premable_length, else: 25
input
|> parse_input()
|> find_first_non_sum(length)
end
def part2(example \\ false) do
input = if example, do: @example, else: File.read!("lib/day9/input.txt")
length = if example, do: @example_premable_length, else: 25
input = parse_input(input)
non_sum = find_first_non_sum(input, length)
{start, length} = find_range_that_sums_to(input, non_sum)
{_, after_start} = Enum.split(input, start)
{range, _} = Enum.split(after_start, length)
Enum.min(range) + Enum.max(range)
end
def parse_input(input) do
input
|> String.trim()
|> String.split("\n")
|> Enum.map(&String.to_integer/1)
end
def find_first_non_sum([_ | rest] = input, length) do
if is_number_sum_of_pairs_first(input, length) do
find_first_non_sum(rest, length)
else
{_, [non_sum | _]} = Enum.split(input, length)
non_sum
end
end
def is_number_sum_of_pairs_first(inputs, count) do
{before, [first | _]} = Enum.split(inputs, count)
before
|> all_pairs()
|> Enum.any?(fn {a, b} ->
# IO.puts("check if #{a} + #{b} == #{first}")
a + b == first
end)
end
def all_pairs(inputs) do
inputs
|> Enum.drop(-1)
|> Enum.with_index()
|> Enum.flat_map(fn {a, a_index} ->
{_, rest} = Enum.split(inputs, a_index + 1)
Enum.map(rest, fn b ->
{a, b}
end)
end)
end
def find_range_that_sums_to([_ | rest] = numbers, target, start \\ 0) do
case do_find_leading_range_that_sums_to(numbers, target) do
false ->
find_range_that_sums_to(rest, target, start + 1)
count when is_integer(count) ->
{start, count}
end
end
def do_find_leading_range_that_sums_to(numbers, remaining, count \\ 0)
def do_find_leading_range_that_sums_to(_, 0, count) do
count
end
def do_find_leading_range_that_sums_to([head | rest], remaining, count) do
# IO.puts("trying start: #{start} count: #{count}, target: #{target}")
if head <= remaining do
do_find_leading_range_that_sums_to(rest, remaining - head, count + 1)
else
false
end
end
end