94 lines
3.0 KiB
Ruby
94 lines
3.0 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# @brief Maps a range from one range to another
|
|
def map_range(source, map_to_range)
|
|
offset = source.first - map_to_range[:source_range].first
|
|
range = source.last - source.first
|
|
(map_to_range[:dest_range].first + offset)..(map_to_range[:dest_range].first + offset + range)
|
|
end
|
|
|
|
# @brief Augments a list of ranges to include all values between the first and last range
|
|
def augment_ranges(ranges, min_val, max_val)
|
|
augmented_ranges = []
|
|
ranges.sort_by! { |range| range[:source_range].first }
|
|
|
|
# Add the first range if it doesn't start at min_val
|
|
if ranges[0][:source_range].first > min_val
|
|
augmented_ranges << {
|
|
source_range: (min_val..(ranges[0][:source_range].first - 1)),
|
|
dest_range: (min_val..(ranges[0][:source_range].first - 1))
|
|
}
|
|
end
|
|
|
|
# Add all ranges between the ranges that are in the list and the ones already in the list
|
|
ranges.each do |range|
|
|
# Add the range that goes from the previous to the current range
|
|
if !augmented_ranges.empty? && (range[:source_range].first > augmented_ranges[-1][:source_range].last + 1)
|
|
augmented_ranges << {
|
|
source_range: ((augmented_ranges[-1][:source_range].last + 1)..(range[:source_range].first - 1)),
|
|
dest_range: ((augmented_ranges[-1][:source_range].last + 1)..(range[:source_range].first - 1))
|
|
}
|
|
end
|
|
augmented_ranges << range
|
|
end
|
|
|
|
# Add the last range if it doesn't end at max_val
|
|
if ranges[-1][:source_range].last < max_val
|
|
augmented_ranges << {
|
|
source_range: ((ranges[-1][:source_range].last + 1)..max_val),
|
|
dest_range: ((ranges[-1][:source_range].last + 1)..max_val)
|
|
}
|
|
end
|
|
augmented_ranges
|
|
end
|
|
|
|
# Start of main program
|
|
f = File.open('./2023/day5/data')
|
|
seeds_in = f.first.strip.split(': ')[1].split(' ').map(&:to_i)
|
|
seeds = []
|
|
|
|
# Convert the seeds into ranges
|
|
i = seeds_in.length - 1
|
|
while i.positive?
|
|
seeds << (seeds_in[i - 1]..(seeds_in[i - 1] + seeds_in[i]))
|
|
i -= 2
|
|
end
|
|
seeds.sort_by!(&:first)
|
|
|
|
ranges = []
|
|
f.each_line do |line|
|
|
next if line.include?('seed') || line.strip.empty?
|
|
|
|
# Map indicates end of previous given mapping, map all seeds to new ranges
|
|
if line.include?('map') || f.eof?
|
|
puts line
|
|
new_seeds = []
|
|
seeds.each do |seed_range|
|
|
last_pos = seed_range.first
|
|
ranges = augment_ranges ranges, seeds[0].first, seeds[-1].last
|
|
ranges.each do |range|
|
|
next if seed_range.first > range[:source_range].last || seed_range.last < range[:source_range].first
|
|
|
|
pos = [seed_range.last, range[:source_range].last].min
|
|
new_seeds << map_range((last_pos..pos), range)
|
|
puts "Mapped #{last_pos..pos} to #{map_range((last_pos..pos), range)}"
|
|
last_pos = pos + 1
|
|
end
|
|
end
|
|
ranges = []
|
|
seeds = new_seeds
|
|
next
|
|
end
|
|
|
|
source_start = line.split(' ')[1].to_i
|
|
dest_start = line.split(' ')[0].to_i
|
|
range = line.split(' ')[2].to_i
|
|
ranges << {
|
|
source_range: (source_start..(source_start + range - 1)),
|
|
dest_range: (dest_start..(dest_start + range - 1))
|
|
}
|
|
end
|
|
|
|
seeds.sort_by!(&:first)
|
|
puts seeds[0].first
|