110 lines
2.7 KiB
Ruby
110 lines
2.7 KiB
Ruby
|
#!/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..#.....#...#
|
||
|
###############
|