Files
advent-of-code/2023/day5/p2.rb
2023-12-05 20:26:25 -05:00

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