# HG changeset patch # User IBBoard # Date 1702151602 0 # Node ID 7826431dbc4f875deabba5aee44b1261bb091b2f # Parent 891878e52a31a318e42cfe28fede4005ac3c27fe Finish day 5 part 2 - seeds with ranges There must be a cleaner method, but this works for splitting ranges when there's a partial overlap. An interim version dropped ranges because we were iterating over an array while also deleting items from it. diff -r 891878e52a31 -r 7826431dbc4f day5b.rb --- a/day5b.rb Sat Dec 09 16:55:59 2023 +0000 +++ b/day5b.rb Sat Dec 09 19:53:22 2023 +0000 @@ -14,9 +14,7 @@ lines = File.open(file, "r").each_line(chomp: true) -seeds = lines.first.split(":")[1].split(" ").map(&:to_i).each_slice(2).map {|v| v[0]..(v[0]+v[1])} - -puts "#{seeds}" +seeds = lines.first.split(":")[1].split(" ").map(&:to_i).each_slice(2).map {|v| v[0]...(v[0]+v[1])} RawMapping = Struct.new(:from, :to, :ranges) MappingRange = Struct.new(:source_range, :offset) @@ -36,35 +34,40 @@ mappings = mappings_array.map {|mapping| [mapping.from, mapping]}.to_h -def map_with_override(mappings, input) - puts "Mappings: #{mappings} - input: #{input}" - mappings.ranges.reduce(input) do |input_ranges, mapping| - puts "Input Ranges: #{input_ranges}; Mapping: #{mapping}" - input_ranges.flat_map do |input_range| - if mapping.source_range.cover?(input_range) - # Input is inside the mapped range - [(input_range.begin+mapping.offset)..(input_range.end+mapping.offset)] - elsif mapping.source_range.end < input_range.begin or input_range.end < mapping.source_range.begin - # Input is entirely outside the mapped range - [input_range] - elsif input_range.begin < mapping.source_range.begin - # Input straddles the start - [ - (input_range.begin+mapping.offset)...(mapping.source_range.begin+mapping.offset), - (mapping.source_range.begin+mapping.offset)..(input_range.end+mapping.offset) - ] - else - # Must straddle the end - [ - (input_range.begin+mapping.offset)...(mapping.source_range.end+mapping.offset), - (mapping.source_range.end+mapping.offset)..(input_range.end+mapping.offset) - ] +def map_with_override(mappings, inputs) + inputs.flat_map do |input| + processed = [] + unprocessed = [input] + # This "each… each" seems messy, but otherwise we can accidentally skip values + # because we're editing as we iterate + mappings.ranges.each do |mapping| + unprocessed.each do |input_range| + if mapping.source_range.end <= input_range.begin or input_range.end <= mapping.source_range.begin + # Input is entirely outside the mapped range + elsif mapping.source_range.cover?(input_range) + # Input is inside the mapped range + processed << ((input_range.begin+mapping.offset)...(input_range.end+mapping.offset)) + unprocessed.delete(input_range) + elsif input_range.begin < mapping.source_range.begin + # Input straddles the start + unprocessed.delete(input_range) + unprocessed << ((input_range.begin)...(mapping.source_range.begin)) + processed << ((mapping.source_range.begin+mapping.offset)...(input_range.end+mapping.offset)) + else + # Must straddle the end + unprocessed.delete(input_range) + processed << ((input_range.begin+mapping.offset)...(mapping.source_range.end+mapping.offset)) + unprocessed << ((mapping.source_range.end)...(input_range.end)) + end end end + processed + unprocessed end end steps = ["seed", "soil", "fertilizer", "water", "light", "temperature", "humidity"] # FIXME: Some negative values and 0s, and the final `min()` gets a nill value -puts steps.reduce(seeds) {|vals, map_name| map_with_override(mappings[map_name], vals)}.map{|v| v.min}.min() +converted_ranges = steps.reduce(seeds) {|vals, map_name| map_with_override(mappings[map_name], vals)} +puts converted_ranges.map{|v| v.min}.min +