1
0
Fork 0
advent-of-code-2024/16/main.rb

110 lines
2.7 KiB
Ruby
Raw Normal View History

2024-12-17 00:23:05 -05:00
#!/usr/bin/env ruby
require "debug"
require "matrix"
@input = (ARGV.first.nil? ? DATA : ARGF)
.readlines(chomp: true)
.map(&:chars)
start = [@input.flatten.index { _1 == "S" }]
.map { Vector[_1 / @input.size, _1 % @input.first.size] }
.first
finish = [@input.flatten.index { _1 == "E" }]
.map { Vector[_1 / @input.size, _1 % @input.first.size] }
.first
NORTH = Vector[-1, 0]
EAST = Vector[0, +1]
SOUTH = Vector[+1, 0]
WEST = Vector[0, -1]
DIRECTIONS = [NORTH, EAST, SOUTH, WEST]
class PriorityQueue < Array
alias extract_min shift
alias peek first
def <<(v)
unless v.is_a?(Array)
raise ArgumentError, "PriorityQueue requires array with priority as last"
end
super
sort_by!(&:last)
end
end
def dijkstra(graph, start, start_direction, target)
dist = Hash.new # scores
prev = Hash.new
queue = PriorityQueue.new
queue << [start, start_direction, 0]
dist[[start, start_direction]] = 0
# alternate only inserting source and add nodes inside score check later
until queue.empty?
position, direction, score = queue.extract_min
neighbors = DIRECTIONS.each do |new_direction|
neighbor = position + new_direction
next if neighbor == position || graph.dig(*neighbor) == "#"
new_score = score + 1 + (direction == new_direction ? 0 : 1) * 1_000
if dist[[neighbor, new_direction]].nil? || new_score < dist[[neighbor, new_direction]]
dist[[neighbor, new_direction]] = new_score
prev[[neighbor, new_direction]] = [[position, direction]]
queue << [neighbor, new_direction, new_score]
elsif new_score == dist[[neighbor, new_direction]]
prev[[neighbor, new_direction]] << [position, direction]
end
end
end
return dist, prev
end
def seats(dist, prev, target)
visited, queue = Array.new, Array.new
lowest_score = dist.keys.select { |(pos, dir)| pos == target }.map { dist[_1] }.min
dist
.select { |(position, _), score| position == target && lowest_score == score }
.each { queue << _1.first }
until queue.empty?
directional_location = queue.pop
visited << directional_location.first
unless prev[directional_location].nil?
queue.concat(prev[directional_location])
end
end
return visited.uniq
end
dist, prev = dijkstra(@input, start, EAST, finish)
part_1 = dist.keys.select { |(pos, dir)| pos == finish }.map { dist[_1] }.min
p part_1
part_2 = seats(dist, prev, finish)
p part_2.size
__END__
###############
#.......#....E#
#.#.###.#.###.#
#.....#.#...#.#
#.###.#####.#.#
#.#.#.......#.#
#.#.#####.###.#
#...........#.#
###.#.#####.#.#
#...#.....#.#.#
#.#.#.###.#.#.#
#.....#...#.#.#
#.###.#.#.#.#.#
#S..#.....#...#
###############