diff --git a/src/advent/day1.rb b/src/advent/day1.rb index a911594..57d6ed5 100644 --- a/src/advent/day1.rb +++ b/src/advent/day1.rb @@ -39,7 +39,7 @@ def get_calibration_code(line) end def get_sum_of_calibration_code(document) - calibration_codes = document.split("\n").hash do |line| + calibration_codes = document.split("\n").map do |line| get_calibration_code line end calibration_codes.reduce 0, :+ diff --git a/src/advent/day2.rb b/src/advent/day2.rb index 4da225f..cd3e6da 100644 --- a/src/advent/day2.rb +++ b/src/advent/day2.rb @@ -43,13 +43,13 @@ class Game def self.deserialize(line) id = /\s*Game\s+(\d+):/.match(line)[1].to_i draws_as_text = line[line.index(":") + 1..] - draws = draws_as_text.split(";").hash(&Draw.method(:deserialize)) + draws = draws_as_text.split(";").map(&Draw.method(:deserialize)) self.new id, draws end def fewer_number_of_cubes [:blue, :red, :green].map do |color| - @draws.hash {|draw| draw.send color } + @draws.map {|draw| draw.send color } .max || 0 end end diff --git a/src/advent/day3.rb b/src/advent/day3.rb index 261fc92..840be26 100644 --- a/src/advent/day3.rb +++ b/src/advent/day3.rb @@ -98,7 +98,7 @@ class EngineLine def extract_engine_numbers digit_indexes = extract_digit_indexes @line - group_digit_indexes(digit_indexes).hash &method(:create_engine_number) + group_digit_indexes(digit_indexes).map &method(:create_engine_number) end def extract_gears @@ -119,13 +119,13 @@ class Engine end def self.deserialize(ser) - els = ser.strip.split("\n").each_with_index.hash { |line, idx| EngineLine.new idx, line} + els = ser.strip.split("\n").each_with_index.map { |line, idx| EngineLine.new idx, line} return self.new els end def extract_engine_numbers @engine_lines - .hash {|line| line.extract_engine_numbers} + .map {|line| line.extract_engine_numbers} .flatten end @@ -159,7 +159,7 @@ class Engine def is_adjacent(engine_number, gear) gear.get_neighbourhood - .hash {|point| engine_number.is_located_on(point[:line_index], point[:column_index])} + .map {|point| engine_number.is_located_on(point[:line_index], point[:column_index])} .any? end @@ -170,15 +170,15 @@ class Engine def extract_gears @engine_lines - .hash {|line| line.extract_gears} + .map {|line| line.extract_gears} .flatten end def get_gear_ratios extract_gears - .hash {|gear| get_adjacent_number_parts(gear)} + .map {|gear| get_adjacent_number_parts(gear)} .filter {|adjacent_numbers| adjacent_numbers.length == 2} - .hash {|adjacent_numbers| adjacent_numbers.hash {|a| a.value}.reduce(1, :*)} + .map {|adjacent_numbers| adjacent_numbers.map {|a| a.value}.reduce(1, :*)} end attr_accessor :engine_lines end @@ -190,6 +190,6 @@ input_content = File.open(input_file_name).read engine = Engine.deserialize input_content -puts engine.extract_engine_numbers_that_touches_symbols.hash {|e| e.value}.sum +puts engine.extract_engine_numbers_that_touches_symbols.map {|e| e.value}.sum puts engine.get_gear_ratios.sum diff --git a/src/advent/day3_test.rb b/src/advent/day3_test.rb index dd2fb00..c55015d 100644 --- a/src/advent/day3_test.rb +++ b/src/advent/day3_test.rb @@ -36,6 +36,7 @@ class Day3Test < Test::Unit::TestCase assert_equal [1, 2] + [3, 4], [1,2,3,4] + # assert_equal ((1..5) + (6..10)).to_a, [1,2,3,4,5,6,7,8,9,10] end end @@ -67,7 +68,7 @@ class EngineLineTest < Test::Unit::TestCase def test_extract_engine_numbers engine = Engine.deserialize @@easy_engine expt = [467, 114, 35, 633, 617, 58, 592, 755, 664, 598] - assert_equal expt, engine.extract_engine_numbers.hash {|e| e.value} + assert_equal expt, engine.extract_engine_numbers.map {|e| e.value} end def test_touches_symbol @@ -79,7 +80,7 @@ class EngineLineTest < Test::Unit::TestCase def test_extract_engine_numbers_that_touches_symbols engine = Engine.deserialize @@easy_engine expt = [467, 35, 633, 617, 592, 755, 664, 598] - assert_equal expt, engine.extract_engine_numbers_that_touches_symbols.hash {|e| e.value} + assert_equal expt, engine.extract_engine_numbers_that_touches_symbols.map {|e| e.value} end def test_empty @@ -96,14 +97,14 @@ class EngineLineTest < Test::Unit::TestCase .........." engine = Engine.deserialize input expt = [] - assert_equal expt, engine.extract_engine_numbers_that_touches_symbols.hash {|e| e.value} + assert_equal expt, engine.extract_engine_numbers_that_touches_symbols.map {|e| e.value} end def test_day_3_part_1_easy engine = Engine.deserialize @@easy_engine expt = 4361 - assert_equal expt, engine.extract_engine_numbers_that_touches_symbols.hash {|e| e.value}.sum + assert_equal expt, engine.extract_engine_numbers_that_touches_symbols.map {|e| e.value}.sum end def test_extract_gears diff --git a/src/advent/day4.rb b/src/advent/day4.rb index 0a3a33e..bfb80d3 100644 --- a/src/advent/day4.rb +++ b/src/advent/day4.rb @@ -19,8 +19,8 @@ class Card def self.deserialize(line) id_match = /Card\s+(\d+)/.match(line) id = id_match[1].to_i - winning_numbers = line[(line.index(":") + 1)..(line.index("|") - 1)].split(/\s+/).filter {|x| !x.empty?}.hash {|x| x.to_i} - selected_numbers = line[(line.index("|") + 1)..line.length].split(/\s+/).filter {|x| !x.empty?}.hash {|x| x.to_i} + winning_numbers = line[(line.index(":") + 1)..(line.index("|") - 1)].split(/\s+/).filter {|x| !x.empty?}.map {|x| x.to_i} + selected_numbers = line[(line.index("|") + 1)..line.length].split(/\s+/).filter {|x| !x.empty?}.map {|x| x.to_i} Card.new id, selected_numbers, winning_numbers end @@ -40,12 +40,12 @@ class Cards cards = ser .split("\n") .filter {|line| !line.empty?} - .hash {|line| Card.deserialize line} + .map {|line| Card.deserialize line} self.new cards end def scores - @cards.hash {|card| card.score} + @cards.map {|card| card.score} end def scratchcards_generated_by_card(card) @@ -57,7 +57,7 @@ class Cards if score > 0 sub_cards = @cards.filter {|sub_card| sub_card.id > card.id and sub_card.id <= card.id + score } ret += sub_cards - .hash {|sub_card| scratchcards_generated_by_card(sub_card)} + .map {|sub_card| scratchcards_generated_by_card(sub_card)} .flatten end @scratchcards_generated_by_card_cache[card.id] = ret diff --git a/src/advent/day5.rb b/src/advent/day5.rb index 994a7da..fc233ec 100644 --- a/src/advent/day5.rb +++ b/src/advent/day5.rb @@ -8,13 +8,25 @@ class WeirMapRange end def includes_source?(source) - @source <= source and source <= (@source + @range_length) + @source <= source and source < (@source + @range_length) + end + + def includes_destination?(destination) + @destination <= destination and destination < (@destination + @range_length) + end + + def source_for_destination(destination) + destination + @source - @destination end def destination_for_source(source) @destination + (source - @source) end + def critical_points + [@source - 1, @source, @source + @range_length, @source + @range_length + 1] + end + def self.deserialize(ser) parts = ser.strip.split(/\s+/).map {|p| p.to_i} self.new parts[0], parts[1], parts[2] @@ -30,6 +42,15 @@ class WeirdMap @weird_map_ranges = weird_map_ranges end + def preimage_of(destination) + @weird_map_ranges.each do |wmr| + if wmr.includes_destination? destination + return wmr.source_for_destination destination + end + end + destination + end + def [](source) @weird_map_ranges.each do |wmr| if wmr.includes_source? source @@ -45,6 +66,12 @@ class WeirdMap .map {|line| WeirMapRange.deserialize line} self.new wmr end + + def critical_points + @weird_map_ranges.map {|r| r.critical_points} .flatten + end + + end @@ -66,6 +93,14 @@ class TodoMap @hash[source] end + def critical_points + @hash.critical_points + end + + def preimage_of(value) + @hash.preimage_of value + end + attr_accessor :from attr_accessor :to attr_accessor :hash @@ -98,12 +133,86 @@ class PlanningMaps attr_accessor :seeds end +class PlanningMapsWithSeedRanges + def initialize(seed_ranges, toto_maps) + @seed_ranges = seed_ranges + @toto_maps = toto_maps + end + + def [](source) + ret = source + @toto_maps.each {|tm| ret = tm[ret]} + ret + end + + def self.deserialize_seed_ranges(ser_arr) + ser_arr.each_slice(2).map {|start, length| (start...(start + length))} + end + + def self.deserialize(ser) + blocks = ser.strip.split(/^$/) + seeds = blocks[0].split(" ")[(1..)].map {|s| s.to_i} + toto_maps = blocks[(1..)].map {|b| TodoMap.deserialize b} + self.new(self.deserialize_seed_ranges(seeds), toto_maps) + end + + def _preimage_of(value, weird_maps) + ret = value + weird_maps.each do |weird_map| + ret = weird_map.preimage_of ret + end + ret + end + + def _critical_points_of(idx) + @toto_maps[idx].critical_points.map {|p| _preimage_of(p, @toto_maps[0...idx])} + end + + def critical_points + (0...@toto_maps.length).map {|idx| _critical_points_of idx}.flatten.uniq.sort + end + + def critical_points_in_range(seed_range) + underlying_critical_points = critical_points + (underlying_critical_points + [seed_range.min, seed_range.max]).filter {|p| seed_range.include? p} + end + + def min_of_seed_range(seed_range) + cpts = critical_points_in_range(seed_range) + cpts.map {|s| self[s]}.min + end + + def all_seeds + @seed_ranges.map {|r| r.to_a}.flatten + end + + def min_destinations + @seed_ranges.map {|seed_range| min_of_seed_range seed_range}.min + end + + def min_destinations_brute_force + all_seeds.map {|s| self[s]}.min + end + + def seed_of(destination) + all_seeds.each do |s| + if self[s] == destination + return s + end + end + end + + attr_accessor :seed_ranges +end + if __FILE__ == $0 input_file_name = File.join(File.dirname(__FILE__), "day_5_input.txt") input_content = File.open(input_file_name).read pms = PlanningMaps.deserialize input_content + pmswsr = PlanningMapsWithSeedRanges.deserialize input_content puts "Solution to part 1: #{pms.min_destinations}" + puts "Solution to part 2: #{pmswsr.min_destinations}" end diff --git a/src/advent/day5_test.rb b/src/advent/day5_test.rb index c64d03f..0d7eacb 100644 --- a/src/advent/day5_test.rb +++ b/src/advent/day5_test.rb @@ -12,6 +12,29 @@ class TestWeirMapRange < Test::Unit::TestCase assert_equal 52, wmr.source assert_equal 2, wmr.range_length end + + def test_happy_path + wmr = WeirMapRange.deserialize "37 52 2" + assert_false wmr.includes_destination? 36 + assert_true wmr.includes_destination? 37 + assert_true wmr.includes_destination? 38 + assert_false wmr.includes_destination? 39 + + + assert_false wmr.includes_source? 51 + assert_true wmr.includes_source? 52 + assert_true wmr.includes_source? 53 + assert_false wmr.includes_source? 54 + + [51, 52, 53, 54].each do |source| + assert_equal(source, wmr.source_for_destination(wmr.destination_for_source(source))) + end + + [36,37,38,39].each do |destination| + assert_equal(destination, wmr.destination_for_source(wmr.source_for_destination(destination))) + end + + end end @@ -91,6 +114,62 @@ humidity-to-location map: def test_day_4_part_1 pms = PlanningMaps.deserialize @@easy_input - assert_equal 2, pms.min_destinations + assert_equal 35, pms.min_destinations end +end + +class TestPlanningMapsWithSeedRanges < Test::Unit::TestCase + + @@easy_input = "seeds: 79 14 55 13 + +seed-to-soil map: +50 98 2 +52 50 48 + +soil-to-fertilizer map: +0 15 37 +37 52 2 +39 0 15 + +fertilizer-to-water map: +49 53 8 +0 11 42 +42 0 7 +57 7 4 + +water-to-light map: +88 18 7 +18 25 70 + +light-to-temperature map: +45 77 23 +81 45 19 +68 64 13 + +temperature-to-humidity map: +0 69 1 +1 0 69 + +humidity-to-location map: +60 56 37 +56 93 4" + + def test_deserialization + pms = PlanningMapsWithSeedRanges.deserialize @@easy_input + expt = (79..92).to_a + (55..67).to_a + assert_equal(expt, pms.seed_ranges.map {|r| r.to_a} .flatten) + end + + def test_brute_force + pms = PlanningMapsWithSeedRanges.deserialize @@easy_input + assert_equal 46, pms.min_destinations_brute_force + assert_equal( 82, pms.seed_of(46)) + end + + def test_day_5_part_2 + pms = PlanningMapsWithSeedRanges.deserialize @@easy_input + assert_equal 46, pms.min_destinations + end + + end \ No newline at end of file