|
|
|
class Solver
|
|
|
|
def initialize(maze)
|
|
|
|
@maze = maze
|
|
|
|
end
|
|
|
|
|
|
|
|
def solve(from, to)
|
|
|
|
seen = Set.new(from)
|
|
|
|
queue = [[from, 0]]
|
|
|
|
current = queue.shift
|
|
|
|
|
|
|
|
loop do
|
|
|
|
location = current[0]
|
|
|
|
break if location == to
|
|
|
|
if seen.include?(location)
|
|
|
|
current = queue.shift
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
seen << location
|
|
|
|
|
|
|
|
neighbors = maze
|
|
|
|
.open_neighbors(location[0], location[1])
|
|
|
|
.reject {|location| seen.include?(location) }
|
|
|
|
|
|
|
|
queue.concat(neighbors.map {|location| [location, current[1] + 1] })
|
|
|
|
|
|
|
|
return nil if queue.empty?
|
|
|
|
current = queue.shift
|
|
|
|
end
|
|
|
|
|
|
|
|
current[1]
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
attr_reader :maze
|
|
|
|
end
|
|
|
|
|
|
|
|
class Maze
|
|
|
|
attr_reader :locations
|
|
|
|
|
|
|
|
def initialize(input)
|
|
|
|
@maze = Hash.new
|
|
|
|
@locations = Hash.new
|
|
|
|
|
|
|
|
x, y = 0, 0
|
|
|
|
input.chars.each do |char|
|
|
|
|
case char
|
|
|
|
when /\d/
|
|
|
|
@locations[char.to_i] = [x,y]
|
|
|
|
char = ?.
|
|
|
|
when "\n"
|
|
|
|
x = -1
|
|
|
|
y += 1
|
|
|
|
else
|
|
|
|
# no-op
|
|
|
|
end
|
|
|
|
|
|
|
|
@maze[[x,y]] = char
|
|
|
|
x += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def [](x,y)
|
|
|
|
maze[[x,y]]
|
|
|
|
end
|
|
|
|
|
|
|
|
def open_neighbors(x, y)
|
|
|
|
[[-1, 0],
|
|
|
|
[ 1, 0],
|
|
|
|
[ 0, -1],
|
|
|
|
[ 0, 1]].map {|dx,dy|
|
|
|
|
[x+dx, y+dy]
|
|
|
|
}.select {|x,y|
|
|
|
|
self[x,y] == ?.
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
attr_reader :maze
|
|
|
|
end
|
|
|
|
|
|
|
|
if __FILE__ == $0
|
|
|
|
maze = Maze.new(ARGF.read)
|
|
|
|
solver = Solver.new(maze)
|
|
|
|
|
|
|
|
hash = maze.locations.to_a.combination(2).each.with_object({}) {|(a, b), hash|
|
|
|
|
steps = solver.solve(a[1], b[1])
|
|
|
|
hash[[a[0],b[0]]] = steps
|
|
|
|
hash[[b[0],a[0]]] = steps
|
|
|
|
}
|
|
|
|
|
|
|
|
p maze
|
|
|
|
.locations
|
|
|
|
.map(&:first)
|
|
|
|
.permutation
|
|
|
|
.select {|route| route[0] == 0 }
|
|
|
|
.map {|route| route + [0] }
|
|
|
|
.map {|route|
|
|
|
|
[route, route.each_cons(2).map {|a,b| hash[[a,b]] }.inject(:+)]
|
|
|
|
}.sort_by(&:last)
|
|
|
|
.first
|
|
|
|
end
|
|
|
|
|
|
|
|
require 'minitest'
|
|
|
|
# require 'minitest/autorun'
|
|
|
|
|
|
|
|
class TestMaze < Minitest::Test
|
|
|
|
def test_maze
|
|
|
|
maze = Maze.new(<<-MAZE)
|
|
|
|
###########
|
|
|
|
#0.1.....2#
|
|
|
|
#.#######.#
|
|
|
|
#4.......3#
|
|
|
|
###########
|
|
|
|
MAZE
|
|
|
|
assert_equal [1,1], maze.locations[0]
|
|
|
|
|
|
|
|
assert_equal [[2,1], [1,2]], maze.open_neighbors(1,1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class TestSolver < Minitest::Test
|
|
|
|
def test_solver
|
|
|
|
maze = Maze.new(<<-MAZE)
|
|
|
|
###########
|
|
|
|
#0.1.....2#
|
|
|
|
#.#######.#
|
|
|
|
#4.......3#
|
|
|
|
###########
|
|
|
|
MAZE
|
|
|
|
solver = Solver.new(maze)
|
|
|
|
assert_equal 2, solver.solve(maze.locations[0], maze.locations[4])
|
|
|
|
end
|
|
|
|
end
|