diff --git a/lib/advent_code2024.ex b/lib/advent_code2024.ex index fa9863d..c9a23ce 100644 --- a/lib/advent_code2024.ex +++ b/lib/advent_code2024.ex @@ -3,89 +3,6 @@ defmodule AdventCode2024 do Documentation for `AdventCode2024`. """ - @doc """ - Reads the input file for day 1 and calculates the total distance between the two lists. - Returns {:ok, result} if successful, {:error, reason} if there's an error. - """ - def solve_day1(input_file \\ "day1/input.txt") do - case File.read(input_file) do - {:ok, content} -> - {left_list, right_list} = parse_input(content) - result = calculate_total_distance(left_list, right_list) - {:ok, result} - {:error, reason} -> {:error, reason} - end - end - - @doc """ - Reads the input file for day 1 part 2 and calculates the similarity score between the two lists. - Returns {:ok, result} if successful, {:error, reason} if there's an error. - """ - def solve_day1_part2(input_file \\ "day1/input.txt") do - case File.read(input_file) do - {:ok, content} -> - {left_list, right_list} = parse_input(content) - result = calculate_similarity_score(left_list, right_list) - {:ok, result} - {:error, reason} -> {:error, reason} - end - end - - @doc """ - Parses the input string into two lists of numbers. - Input format is expected to be tab-separated numbers, one pair per line. - """ - def parse_input(content) do - {left, right} = - content - |> String.split("\n", trim: true) - |> Enum.map(&String.split(&1, ~r/\s+/, trim: true)) - |> Enum.map(fn [left, right] -> - {String.to_integer(left), String.to_integer(right)} - end) - |> Enum.unzip() - - {left, right} - end - - @doc """ - Calculates the total distance between two lists of numbers. - Lists are first sorted, then paired up, and the absolute differences are summed. - - ## Examples - - iex> AdventCode2024.calculate_total_distance([3, 4, 2, 1, 3, 3], [4, 3, 5, 3, 9, 3]) - 11 - iex> AdventCode2024.calculate_total_distance([], []) - 0 - iex> AdventCode2024.calculate_total_distance([1], [3]) - 2 - - """ - def calculate_total_distance(left_list, right_list) do - Enum.zip(Enum.sort(left_list), Enum.sort(right_list)) - |> Enum.map(fn {a, b} -> abs(a - b) end) - |> Enum.sum() - end - - @doc """ - Calculates the similarity score between two lists. - For each number in the left list, multiplies it by the number of times it appears in the right list. - Returns the sum of all these products. - - ## Examples - - iex> AdventCode2024.calculate_similarity_score([3, 4, 2, 1, 3, 3], [4, 3, 5, 3, 9, 3]) - 31 - - """ - def calculate_similarity_score(left_list, right_list) do - frequencies = Enum.frequencies(right_list) - - left_list - |> Enum.map(fn num -> - num * Map.get(frequencies, num, 0) - end) - |> Enum.sum() - end + defdelegate solve_day1(input_file \\ "day1/input.txt"), to: AdventCode2024.Day1, as: :solve + defdelegate solve_day1_part2(input_file \\ "day1/input.txt"), to: AdventCode2024.Day1, as: :solve_part2 end diff --git a/lib/advent_code2024/day1.ex b/lib/advent_code2024/day1.ex new file mode 100644 index 0000000..54881a4 --- /dev/null +++ b/lib/advent_code2024/day1.ex @@ -0,0 +1,54 @@ +defmodule AdventCode2024.Day1 do + @moduledoc """ + Solution for Advent of Code 2024 - Day 1: Historian Hysteria + """ + + def solve(input_file \\ "day1/input.txt") do + case File.read(input_file) do + {:ok, content} -> + {left_list, right_list} = parse_input(content) + result = calculate_total_distance(left_list, right_list) + {:ok, result} + {:error, reason} -> {:error, reason} + end + end + + def solve_part2(input_file \\ "day1/input.txt") do + case File.read(input_file) do + {:ok, content} -> + {left_list, right_list} = parse_input(content) + result = calculate_similarity_score(left_list, right_list) + {:ok, result} + {:error, reason} -> {:error, reason} + end + end + + defp parse_input(content) do + {left, right} = + content + |> String.split("\n", trim: true) + |> Enum.map(&String.split(&1, ~r/\s+/, trim: true)) + |> Enum.map(fn [left, right] -> + {String.to_integer(left), String.to_integer(right)} + end) + |> Enum.unzip() + + {left, right} + end + + defp calculate_total_distance(left_list, right_list) do + Enum.zip(Enum.sort(left_list), Enum.sort(right_list)) + |> Enum.map(fn {a, b} -> abs(a - b) end) + |> Enum.sum() + end + + defp calculate_similarity_score(left_list, right_list) do + frequencies = Enum.frequencies(right_list) + + left_list + |> Enum.map(fn num -> + num * Map.get(frequencies, num, 0) + end) + |> Enum.sum() + end +end diff --git a/test/advent_code2024_test.exs b/test/advent_code2024_test.exs index 2137679..e9f2134 100644 --- a/test/advent_code2024_test.exs +++ b/test/advent_code2024_test.exs @@ -1,6 +1,7 @@ defmodule AdventCode2024Test do use ExUnit.Case doctest AdventCode2024 + alias AdventCode2024.Day1 describe "Day 1: Historian Hysteria" do test "solves day 1 puzzle with actual input file" do @@ -12,46 +13,9 @@ defmodule AdventCode2024Test do assert {:error, :enoent} = AdventCode2024.solve_day1("nonexistent.txt") end - test "parses input string correctly" do - input = "1\t2\n3\t4\n5\t6" - assert {[1, 3, 5], [2, 4, 6]} = AdventCode2024.parse_input(input) - end - - test "calculates total distance between two lists using example input" do - left_list = [3, 4, 2, 1, 3, 3] - right_list = [4, 3, 5, 3, 9, 3] - - assert AdventCode2024.calculate_total_distance(left_list, right_list) == 11 - end - - test "handles empty lists" do - assert AdventCode2024.calculate_total_distance([], []) == 0 - end - - test "handles single element lists" do - assert AdventCode2024.calculate_total_distance([1], [3]) == 2 - end end describe "Day 1 Part 2: Similarity Score" do - test "calculates similarity score using example input" do - left_list = [3, 4, 2, 1, 3, 3] - right_list = [4, 3, 5, 3, 9, 3] - - assert AdventCode2024.calculate_similarity_score(left_list, right_list) == 31 - end - - test "handles empty lists for similarity score" do - assert AdventCode2024.calculate_similarity_score([], []) == 0 - end - - test "handles lists with no matches" do - assert AdventCode2024.calculate_similarity_score([1, 2], [3, 4]) == 0 - end - - test "handles lists with all matches" do - assert AdventCode2024.calculate_similarity_score([1, 1], [1, 1]) == 4 - end test "solves day 1 part 2 puzzle with actual input file" do assert {:ok, result} = AdventCode2024.solve_day1_part2()