maze-solver 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +48 -0
- data/Rakefile +6 -0
- data/bin/maze-solver +27 -0
- data/etc/demo.maze +14 -0
- data/lib/maze_solver.rb +74 -0
- data/lib/maze_solver/version.rb +5 -0
- data/maze_solver.gemspec +19 -0
- data/spec/maze_spec.rb +106 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/test.maze +7 -0
- metadata +63 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Pete Johns
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Maze Solver
|
2
|
+
|
3
|
+
Written as an exercise in applying Ruby, this project solves mazes:
|
4
|
+
|
5
|
+
maze-solver etc/demo.maze 18 0 38 0
|
6
|
+
About to solve your 40x14 maze from (18, 0) to (38,0).
|
7
|
+
GO!
|
8
|
+
Solved!
|
9
|
+
******************.*******************.*
|
10
|
+
* .......****.** .......****.*
|
11
|
+
* *****.*****...**.** *****.*****...**.*
|
12
|
+
* *****.*******.**.** *****.*******.**.*
|
13
|
+
* * ......**.**.** * ......**.**.*
|
14
|
+
* **********.**....** **********.**....*
|
15
|
+
************.*******************.*******
|
16
|
+
************.***** *************.***** *
|
17
|
+
* ..****............... **** *
|
18
|
+
* ***** *****...**.** ***** ***** ** *
|
19
|
+
* ***** *******.**.** ***** ******* ** *
|
20
|
+
* * **.**.** * ** ** *
|
21
|
+
* ********** **....** ********** ** *
|
22
|
+
****************************************
|
23
|
+
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
$ gem install maze-solver
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
maze-solver filename start_x start_y end_x end_y
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create new Pull Request
|
40
|
+
|
41
|
+
## Thanks
|
42
|
+
|
43
|
+
If you find this stuff useful, please follow this repository on
|
44
|
+
[GitHub](https://github.com/johnsyweb/ruby_maze_solver). If you have something to say,
|
45
|
+
you can contact [johnsyweb](http://johnsy.com/about/) on
|
46
|
+
[Twitter](http://twitter.com/johnsyweb/) and
|
47
|
+
[GitHub](https://github.com/johnsyweb/).
|
48
|
+
|
data/Rakefile
ADDED
data/bin/maze-solver
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'maze_solver'
|
7
|
+
|
8
|
+
def main(filename, start_x, start_y, end_x, end_y)
|
9
|
+
maze = MazeSolver::MazeSolver.new(from_file: filename)
|
10
|
+
puts "About to solve your #{maze.width}x#{maze.height} maze from " +
|
11
|
+
"(#{start_x}, #{start_y}) to (#{end_x},#{end_y})."
|
12
|
+
puts 'GO!'
|
13
|
+
if maze.solve start_x.to_i, start_y.to_i, end_x.to_i, end_y.to_i
|
14
|
+
puts 'Solved!'
|
15
|
+
else
|
16
|
+
puts 'You beat me!'
|
17
|
+
end
|
18
|
+
puts maze
|
19
|
+
end
|
20
|
+
|
21
|
+
if ARGV.length == 5
|
22
|
+
filename, start_x, start_y, end_x, end_y = ARGV
|
23
|
+
main(filename, start_x, start_y, end_x, end_y)
|
24
|
+
else
|
25
|
+
puts "Usage: #{$PROGRAM_NAME} filename start_x start_y end_x end_y\n"
|
26
|
+
puts "Example: #{$PROGRAM_NAME} demo.maze 18 0 38 0\n"
|
27
|
+
end
|
data/etc/demo.maze
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
****************** ******************* *
|
2
|
+
* **** ** **** *
|
3
|
+
* ***** ***** ** ** ***** ***** ** *
|
4
|
+
* ***** ******* ** ** ***** ******* ** *
|
5
|
+
* * ** ** ** * ** ** *
|
6
|
+
* ********** ** ** ********** ** *
|
7
|
+
************ ******************* *******
|
8
|
+
************ ***** ************* ***** *
|
9
|
+
* **** **** *
|
10
|
+
* ***** ***** ** ** ***** ***** ** *
|
11
|
+
* ***** ******* ** ** ***** ******* ** *
|
12
|
+
* * ** ** ** * ** ** *
|
13
|
+
* ********** ** ** ********** ** *
|
14
|
+
****************************************
|
data/lib/maze_solver.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'maze_solver/version'
|
4
|
+
|
5
|
+
module MazeSolver
|
6
|
+
|
7
|
+
class MazeSolver
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
if args[:from_grid]
|
11
|
+
@grid = args[:from_grid].split("\n")
|
12
|
+
elsif args[:from_file]
|
13
|
+
@grid = from_file(args[:from_file])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def height
|
18
|
+
@grid.size
|
19
|
+
end
|
20
|
+
|
21
|
+
def width
|
22
|
+
@grid.map { |row| row.length }.max
|
23
|
+
end
|
24
|
+
|
25
|
+
def visitable?(x, y)
|
26
|
+
row, column = y, x
|
27
|
+
return false unless (0...height).include? row
|
28
|
+
return false unless (0...width).include? column
|
29
|
+
@grid[row][column].chr == ' '
|
30
|
+
end
|
31
|
+
|
32
|
+
def visit(x, y)
|
33
|
+
row, column = y, x
|
34
|
+
@grid[row][column] = '.'
|
35
|
+
end
|
36
|
+
|
37
|
+
def unvisit(x, y)
|
38
|
+
row, column = y, x
|
39
|
+
@grid[row][column] = ' '
|
40
|
+
end
|
41
|
+
|
42
|
+
def solve(start_x, start_y, end_x, end_y)
|
43
|
+
return false unless visitable?(start_x, start_y)
|
44
|
+
|
45
|
+
visit start_x, start_y
|
46
|
+
|
47
|
+
return true if [start_x, start_y] == [end_x, end_y]
|
48
|
+
|
49
|
+
return true if adjacent_cell_solvable?(start_x, start_y, end_x, end_y)
|
50
|
+
|
51
|
+
unvisit(start_x, start_y)
|
52
|
+
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def adjacent_cell_solvable?(start_x, start_y, end_x, end_y)
|
57
|
+
solve(start_x + 1, start_y, end_x, end_y) ||
|
58
|
+
solve(start_x, start_y + 1, end_x, end_y) ||
|
59
|
+
solve(start_x - 1, start_y, end_x, end_y) ||
|
60
|
+
solve(start_x, start_y - 1, end_x, end_y)
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
@grid.join("\n")
|
65
|
+
end
|
66
|
+
|
67
|
+
def from_file(filename)
|
68
|
+
@grid = File.open(filename, 'r').readlines
|
69
|
+
@grid.each { | row | row.strip! }
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
data/maze_solver.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'maze_solver/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'maze-solver'
|
8
|
+
gem.version = MazeSolver::VERSION
|
9
|
+
gem.authors = ['Pete Johns']
|
10
|
+
gem.email = ['paj-github@johnsy.com']
|
11
|
+
gem.description = 'A gem to solve a 2D maze.'
|
12
|
+
gem.summary = 'Written as an exercise'
|
13
|
+
gem.homepage = 'https://github.com/johnsyweb/ruby_maze_solver#readme'
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ['lib']
|
19
|
+
end
|
data/spec/maze_spec.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe MazeSolver do
|
6
|
+
def given_a_simple_8_x_3_maze
|
7
|
+
@maze = MazeSolver::MazeSolver.new(from_grid: "********\n" +
|
8
|
+
" \n" +
|
9
|
+
"********\n"
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should have a width of 8' do
|
14
|
+
given_a_simple_8_x_3_maze
|
15
|
+
@maze.width.should eq 8
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should have a height of 3' do
|
19
|
+
given_a_simple_8_x_3_maze
|
20
|
+
@maze.height.should eq 3
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should be possible to visit an empty space' do
|
24
|
+
given_a_simple_8_x_3_maze
|
25
|
+
@maze.visitable?(0, 1).should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should not be possible to visit a wall' do
|
29
|
+
given_a_simple_8_x_3_maze
|
30
|
+
@maze.visitable?(0, 0).should be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should not be possible to visit outside the maze' do
|
34
|
+
given_a_simple_8_x_3_maze
|
35
|
+
@maze.visitable?(8, 3).should be_false
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should not be possible to visit a visited space' do
|
39
|
+
given_a_simple_8_x_3_maze
|
40
|
+
@maze.visit 0, 1
|
41
|
+
@maze.visitable?(0, 1).should be_false
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should be possible to visit an unvisited space' do
|
45
|
+
given_a_simple_8_x_3_maze
|
46
|
+
@maze.visit 0, 1
|
47
|
+
@maze.unvisit 0, 1
|
48
|
+
@maze.visitable?(0, 1).should be_true
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should solve a simple maze' do
|
52
|
+
given_a_simple_8_x_3_maze
|
53
|
+
@maze.solve(0, 1, 7, 1).should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should solve a simple maze backwards' do
|
57
|
+
given_a_simple_8_x_3_maze
|
58
|
+
@maze.solve(7, 1, 0, 1).should be_true
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'should solve a bigger maze' do
|
62
|
+
maze = MazeSolver::MazeSolver.new(from_grid: "********\n" +
|
63
|
+
" ** *\n" +
|
64
|
+
"* ** * *\n" +
|
65
|
+
"* * * *\n" +
|
66
|
+
"* ** * *\n" +
|
67
|
+
"* **** *\n" +
|
68
|
+
"* *\n" +
|
69
|
+
"********\n")
|
70
|
+
maze.solve(0, 1, 3, 3).should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should fail an impossible maze' do
|
74
|
+
maze = MazeSolver::MazeSolver.new(from_grid: "********\n" +
|
75
|
+
" ** *\n" +
|
76
|
+
"* **** *\n" +
|
77
|
+
"* * * *\n" +
|
78
|
+
"* ** * *\n" +
|
79
|
+
"* **** *\n" +
|
80
|
+
"* *\n" +
|
81
|
+
"********\n")
|
82
|
+
maze.solve(0, 1, 3, 3).should be_false
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should be printable' do
|
86
|
+
given_a_simple_8_x_3_maze
|
87
|
+
@maze.to_s.should eq "********\n" +
|
88
|
+
" \n" +
|
89
|
+
'********'
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should be printable when solved' do
|
93
|
+
given_a_simple_8_x_3_maze
|
94
|
+
@maze.solve 0, 1, 7, 1
|
95
|
+
@maze.to_s.should eq "********\n" +
|
96
|
+
"........\n" +
|
97
|
+
'********'
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'should be creatable from a file' do
|
101
|
+
maze = MazeSolver::MazeSolver.new(from_file: 'spec/test.maze')
|
102
|
+
maze.solve(18, 0, 12, 6).should be_true
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'maze_solver'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
9
|
+
config.run_all_when_everything_filtered = true
|
10
|
+
config.filter_run :focus
|
11
|
+
config.order = 'random'
|
12
|
+
end
|
data/spec/test.maze
ADDED
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: maze-solver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Pete Johns
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-19 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A gem to solve a 2D maze.
|
15
|
+
email:
|
16
|
+
- paj-github@johnsy.com
|
17
|
+
executables:
|
18
|
+
- maze-solver
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- .rspec
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE.txt
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- bin/maze-solver
|
29
|
+
- etc/demo.maze
|
30
|
+
- lib/maze_solver.rb
|
31
|
+
- lib/maze_solver/version.rb
|
32
|
+
- maze_solver.gemspec
|
33
|
+
- spec/maze_spec.rb
|
34
|
+
- spec/spec_helper.rb
|
35
|
+
- spec/test.maze
|
36
|
+
homepage: https://github.com/johnsyweb/ruby_maze_solver#readme
|
37
|
+
licenses: []
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.8.23
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: Written as an exercise
|
60
|
+
test_files:
|
61
|
+
- spec/maze_spec.rb
|
62
|
+
- spec/spec_helper.rb
|
63
|
+
- spec/test.maze
|