maze-solver 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|