mirror of
https://github.com/rjNemo/ai_advent_code_2024
synced 2026-06-06 02:26:44 +00:00
refactor: Move Day 1 logic into dedicated module
refactor: Limit public API of Day1 module and remove private function tests
This commit is contained in:
parent
bbf027d22a
commit
992008b34c
3 changed files with 57 additions and 122 deletions
|
|
@ -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
|
||||
|
|
|
|||
54
lib/advent_code2024/day1.ex
Normal file
54
lib/advent_code2024/day1.ex
Normal file
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue