You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
advent-of-code/2022/ruby/day_12.rb

50 lines
1.2 KiB

class HeightMap
NEIGHBORS = [[0, -1], [-1, 0], [0, 1], [1, 0]]
def initialize(heights)
@heights = heights
end
def shortest(from:, to:, &cond)
frontier = [from]
visited = { from => 0 }
until frontier.empty? || to.any? { visited.has_key?(_1) }
current = frontier.shift
NEIGHBORS.each do |delta|
candidate = current.zip(delta).map { _1 + _2 }
next if visited.has_key?(candidate)
next unless cand_height = @heights[candidate]
next unless cond.(@heights.fetch(current), cand_height)
visited[candidate] = visited.fetch(current) + 1
frontier << candidate
end
frontier.sort_by { visited.fetch(_1) }
end
visited.find {|k,v| to.include?(k) }.last
end
end
heights = ARGF.read.lines(chomp: true).map(&:chars)
.flat_map.with_index {|row,y|
row.map.with_index {|height,x| [[y, x], height] }
}.to_h
s, e = heights.invert.values_at(?S, ?E)
heights[s] = ?a
heights[e] = ?z
hm = HeightMap.new(heights)
# part one
p hm.shortest(from: s, to: [e]) { _1.ord + 1 >= _2.ord }
# part two
as = heights.select { _2 == ?a }.map(&:first)
p hm.shortest(from: e, to: as) { _1.ord - 1 <= _2.ord }