Skip to content

Instantly share code, notes, and snippets.

@AgileMantis
Created July 19, 2012 13:19
Show Gist options
  • Save AgileMantis/3143867 to your computer and use it in GitHub Desktop.
Save AgileMantis/3143867 to your computer and use it in GitHub Desktop.
Ruby Breadth-first Search algorithm (implemented to solve a maze)
# (The MIT License)
#
# Copyright (c) 2012 Ledsworth Consulting LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
module Maze
# Mazes are hydrated into a two dimensional array, with "#"s as walls
# and spaces as pathways. Entrance and exit points are simply spaces
# on the left and right walls (they cannot be on the top or bottom,
# but the code could be changed to accommodate that).#
# Store mazes in simple text files.
#
# Example:
#
# ###########
# # #
# # ##### # #
# # # #
# ### # ### #
# # # #
# # # ### ###
# # # #
# # ### # # #
# # # #
# ###########
#
# Example:
#
# require './maze_solver'
# maze = Maze.load('./mymaze.txt')
# maze_solver = Maze::Solver.new(maze)
# maze_solver.solve
#
# ruby maze_runner.rb mymaze.txt
#
# Passing true in as a second argument will produce output
# step by step, so you can visually trace the progression.
#
# require './maze_solver'
# maze = Maze.load('./mymaze.txt')
# maze_solver = Maze::Solver.new(maze,true)
# maze_solver.solve
#
def self.load(file)
maze = {}
matrix = []
File.open(file) do |file|
file.each_line do |line|
matrix << line.chomp!.split(//)
end
end
maze[:matrix] = matrix
set_entry_exit(maze)
maze
end
def self.set_entry_exit(maze)
matrix = maze[:matrix]
maze[:entrance_x] = 0
maze[:exit_x] = matrix[0].size-1
matrix.each_index do |idx|
maze[:entrance_y] = idx if matrix[idx][0]==' '
maze[:exit_y] = idx if matrix[idx][matrix[0].size-1]==' '
end
err = <<-ERROR
Exists must be on the left and right side of the maze
ERROR
raise err unless maze.size == 5
end
# Square to hold an x,y square during maze traversal, with
# a link back to it's parent. Links are used after the exit
# is found to trace exit back to entrance.
class Sqr < Struct.new(:x, :y, :parent)
end
class Solver
def initialize(maze,show_progress=false)
@maze = maze
@show_progress = show_progress
end
def print
@maze[:matrix].each do |line|
puts line.join
end
puts
end
# Main BFS algorithm.
def solve
matrix = @maze[:matrix]
exit_found = false
sqr = Sqr.new(@maze[:entrance_x], @maze[:entrance_y], nil)
queue = [] # Queue.new
queue << sqr
while !queue.empty? && !exit_found
sqr = queue.shift # queue.pop
x = sqr.x
y = sqr.y
if (x == @maze[:exit_x] && y == @maze[:exit_y])
exit_found = true
else
matrix[y][x] = '-' # Mark path as visited
print if @show_progress
queue << Sqr.new(x+1,y,sqr) if open_square(x+1,y,matrix)
queue << Sqr.new(x-1,y,sqr) if open_square(x-1,y,matrix)
queue << Sqr.new(x,y+1,sqr) if open_square(x,y+1,matrix)
queue << Sqr.new(x,y-1,sqr) if open_square(x,y-1,matrix)
end
end
# Clear all pathway markers
clear_matrix
if exit_found
# Repaint solution pathway with markers
matrix[sqr.y][sqr.x] = '>'
while sqr.parent
sqr = sqr.parent
matrix[sqr.y][sqr.x] = '-'
end
puts "Maze solved:\n\n"
print
else
puts 'No exit found'
end
end
private
def open_square(x,y,matrix)
return false if (x<0 || x>matrix[0].size-1 || y<0 || y>matrix.size-1)
return matrix[y][x] == ' '
end
def clear_matrix
@maze[:matrix].each_index do |idx|
@maze[:matrix][idx] = @maze[:matrix][idx].join.gsub(/[^#|\s]/i,' ').split(//)
end
end
end
end
@sakthivelsekar33
Copy link

line @ 64 : matrix << line.chomp!.split(//)

chomp! returns nil object.

I thin it should be changed to matrix << line.chomp.split(//)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment