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.
94 lines
2.0 KiB
94 lines
2.0 KiB
require "set"
|
|
|
|
walls = Set.new
|
|
blizzards = Hash.new {|h,k| h[k] = Set.new }
|
|
clear_ground = Set.new
|
|
|
|
ARGF.read.lines(chomp: true).each.with_index do |row, y|
|
|
row.chars.each.with_index do |pos,x|
|
|
case pos
|
|
when ?#
|
|
walls.add([y,x])
|
|
when /[v^<>]/
|
|
blizzards[pos].add([y,x])
|
|
when ?.
|
|
clear_ground.add([y,x])
|
|
else
|
|
fail pos
|
|
end
|
|
end
|
|
end
|
|
|
|
x_range = Range.new(*walls.map(&:last).minmax)
|
|
y_range = Range.new(*walls.map(&:first).minmax)
|
|
|
|
debug = ->() do
|
|
puts
|
|
puts y_range.map {|y|
|
|
x_range.map {|x|
|
|
if walls.include?([y,x])
|
|
?#
|
|
elsif blizzards.any? {|_,pos| pos.include?([y,x])}
|
|
b = blizzards.select {|_,pos| pos.include?([y,x])}
|
|
(b.size == 1) ? b.keys[0] : b.size
|
|
else
|
|
?.
|
|
end
|
|
}.join
|
|
}.join("\n")
|
|
end
|
|
|
|
start = [0, clear_ground.find {|y,x| y == 0 }.last]
|
|
goal = [y_range.end, clear_ground.find {|y,x| y == y_range.end }.last]
|
|
|
|
dir_deltas = {
|
|
?^ => [-1, 0],
|
|
?v => [ 1, 0],
|
|
?< => [ 0, -1],
|
|
?> => [ 0, 1],
|
|
}
|
|
moves = [*dir_deltas.values, [0, 0]]
|
|
|
|
frontier = [start]
|
|
(1..).each do |time|
|
|
if (time % 1).zero?
|
|
puts time
|
|
# p frontier
|
|
# debug.()
|
|
end
|
|
|
|
blizzards = blizzards.to_h {|dir,positions|
|
|
delta = dir_deltas.fetch(dir)
|
|
edges, positions = positions
|
|
.map {|pos| pos.zip(delta).map { _1 + _2 }}
|
|
.partition { walls.include?(_1) }
|
|
positions.concat(edges.map {|(y,x)|
|
|
case dir
|
|
when ?^ then [y_range.end - 1, x]
|
|
when ?v then [y_range.begin + 1, x]
|
|
when ?< then [y, x_range.end - 1]
|
|
when ?> then [y, x_range.end + 1]
|
|
else fail dir
|
|
end
|
|
})
|
|
[dir, positions]
|
|
}
|
|
|
|
clear_ground = y_range.map {|y| x_range.map {|x| [y, x] }}.flatten(1)
|
|
.reject {|pos| walls.include?(pos) || blizzards.any? { _2.include?(pos) }}
|
|
|
|
options = frontier.map {|current|
|
|
moves.filter_map {|delta|
|
|
pos = current.zip(delta).map { _1 + _2 }
|
|
clear_ground.include?(pos) && pos
|
|
}
|
|
}.flatten(1)
|
|
|
|
frontier = options.uniq
|
|
|
|
if frontier.include?(goal)
|
|
puts time + 1
|
|
exit
|
|
end
|
|
end
|