Finished day 5, part 2!

This commit is contained in:
didier amyot committed 2023-12-05 07:15:28 -05:00
1 parent 1d19b0548a
commit 4392344bc4
7 files changed
+211 -22

No files matched your search

+1 -1
View File
@@ -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, :+
+2 -2
View File
@@ -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
+8 -8
View File
@@ -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
+5 -4
View File
@@ -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
+5 -5
View File
@@ -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
+110 -1
View File
@@ -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
+80 -1
View File
@@ -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