I decided that this year is the year. I will finish the AoC (in the past, I always had something else come up after a few days in). Since I want to finish it (and I’m already six days late) I decided to go with the language I know and like the most: Ruby.
The task
The task is to compare two lists of numbers, select the smallest number in both lists and calculate the distance between them. Then, select the pair of second-smallest numbers, and so on. The result is the total distance between the numbers (the sum of all the distances). For example:
Smallest numbers from both lists: left: 5, right: 3 -> distance: 2
Second smallest: left: 2, right: 4 -> distance: 2
Total distance: 4 etc.
And this is how the example data looks like:
3 4
4 3
2 5
1 3
3 9
3 3
The total distance for this dataset is 11.
Write-up
The start is easy—just read the file. I tend to overthink simple things, so I started by figuring out how to parse and split the values into two arrays.
Well, I quickly realized that I didn’t need to. Just use Ruby’s default String#split method to split on whitespace and/or newlines. Then parse the values to integers, and voilà! We have one big array of numbers.
At this point, I could iterate over the array and move the values into separate arrays one by one, but that’s no fun. Ruby has a lot of helper methods that make mundane tasks a breeze..
One of them is partition. According to the documentation:
Returns two arrays, the first containing the elements of enum for which the block evaluates to true, the second containing the rest.
Easy-peasy. I just needed to check whether the value is even or odd, and bam! Two arrays. THANKFULLY, my brain still works at 11 o’clock at night, and I realized I actually needed to check whether the index is even or odd.
Alright. I now have two arrays with correctly split values, so I can start comparing.
The initial idea was simple: find the smallest value in both lists, compare them, calculate the distance, and add it to a sum variable (initialized to 0).
It sounds good, and it will work, but that involves a lot of looping over the same arrays. Even using the built-in min method involves looping (it’s implemented in C, so it’s quick, but it’s still looping).
For a moment, I even considered caching the values I’d already seen, but that’s the overthinking part of me kicking in.
The easiest solution is to just sort both arrays and loop over them once.
One last thing to keep in mind: no matter what the result of the subtraction is, the distance is always zero or a positive number. This means we need the absolute value.
Here’s the final implementation: (keep in mind that this is not a production code, it definitely could be improved, but for AoC it’s good and fast enough)
input = File.read("puzzle_input.txt") # The proper test data from AoC
# input = File.read "example.txt" # that's the example input I included in the post
numbers = input.split.map(&:to_i)
sum = 0
left, right = numbers.partition.with_index { |_, i| i.even? }
left.sort!
right.sort!
left.each_with_index do |e, i|
sum += (e - right[i]).abs
end
puts sum