mazemap 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rpsec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +46 -0
- data/Rakefile +6 -0
- data/bin/mazemap +16 -0
- data/bin/setup +8 -0
- data/lib/mazemap.rb +22 -0
- data/lib/mazemap/graph.rb +104 -0
- data/lib/mazemap/pathfinder.rb +78 -0
- data/lib/mazemap/version.rb +3 -0
- data/mazemap.gemspec +37 -0
- metadata +156 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1de2a8d8c405234dd72965b2791be516d8a29624
|
4
|
+
data.tar.gz: bbcee69ecb9a45efe837cfc08eb6ab4fb1144704
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '0978d9f462c4aacab6d8cb61fdc1413f1edd8014a9b9d6dc0be1957aff14a8d875c7169d1e93165ceccbe341f624d72b3cd122c9ad2c759fb433947ef1ecbeb2'
|
7
|
+
data.tar.gz: dcad932c2f9910bbc4703e54846f507d5e37f8b9c07b6368e190eeebf943bfa74037bedf63deb60d37b0777c269a6974a960afc4dd1e5b25b8f058552f79759a
|
data/.gitignore
ADDED
data/.rpsec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Evgeniy
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
## Mazemap Pathfinder
|
2
|
+
Mazemap allows you to search for a shortest path in the given 2D map(check tmp/maze.txt for examples)
|
3
|
+
|
4
|
+
Mazemap has built-in command line tool
|
5
|
+
So after the gem installation you can just type
|
6
|
+
`bundle exec mazemap maze.txt`
|
7
|
+
to execute a search
|
8
|
+
|
9
|
+
#### Usage
|
10
|
+
Gemfile
|
11
|
+
`gem 'mazemap', github: 'leafmind/mazemap'`
|
12
|
+
|
13
|
+
`bundle install`
|
14
|
+
|
15
|
+
In your code:
|
16
|
+
`Mazemap.read_and_search(filename)`
|
17
|
+
|
18
|
+
Also you can use mazemap command line tool:
|
19
|
+
`bundle exec mazemap maze.txt`
|
20
|
+
|
21
|
+
#### Maze file format example
|
22
|
+
|
23
|
+
```
|
24
|
+
******************************************
|
25
|
+
* ****** *** *
|
26
|
+
* *** *** ****** * B *** *
|
27
|
+
* *** *** ****** *** *
|
28
|
+
* *** ***** *** *
|
29
|
+
* ********* ***** *** *
|
30
|
+
* ******* **** *** *** *
|
31
|
+
* ****** *
|
32
|
+
* *** **** ********** *
|
33
|
+
* *** *********** *
|
34
|
+
* ******* ************ *
|
35
|
+
* ******* **** *
|
36
|
+
* A **** ******
|
37
|
+
******************************************
|
38
|
+
```
|
39
|
+
|
40
|
+
|
41
|
+
Known issues on MAC OS X
|
42
|
+
https://github.com/SciRuby/nmatrix/issues/426
|
43
|
+
|
44
|
+
Workaround
|
45
|
+
https://github.com/SciRuby/nmatrix/issues/426#issuecomment-218122512
|
46
|
+
|
data/Rakefile
ADDED
data/bin/mazemap
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require 'mazemap'
|
5
|
+
|
6
|
+
filename = ARGV[0]
|
7
|
+
|
8
|
+
if filename
|
9
|
+
puts Mazemap.read_and_search(filename).inspect
|
10
|
+
else
|
11
|
+
puts <<-EOF
|
12
|
+
Please specify input file as command line parameter
|
13
|
+
USAGE: bundle exec mazemap /path/to/your/file
|
14
|
+
File should contain maze in described format(see tmp/maze.txt)
|
15
|
+
EOF
|
16
|
+
end
|
data/bin/setup
ADDED
data/lib/mazemap.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'nmatrix'
|
2
|
+
require 'mazemap/graph'
|
3
|
+
require 'mazemap/pathfinder'
|
4
|
+
require 'mazemap/version'
|
5
|
+
|
6
|
+
# Tries to find the shortest path
|
7
|
+
#
|
8
|
+
# @author Evgenii Shevchenko
|
9
|
+
# @since 0.0.1
|
10
|
+
module Mazemap
|
11
|
+
# Search for a shortest paths
|
12
|
+
#
|
13
|
+
# @example Run search
|
14
|
+
# >> Mazemap.read_and_search(filename)
|
15
|
+
# => [[6, 11], [7, 11], [8, 11], [9, 11], [10, 11]]
|
16
|
+
def read_and_search(filename)
|
17
|
+
pathfinder = Pathfinder.new(filename)
|
18
|
+
pathfinder.solution
|
19
|
+
end
|
20
|
+
|
21
|
+
module_function :read_and_search
|
22
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Mazemap
|
2
|
+
# Graph object class
|
3
|
+
#
|
4
|
+
# @author Evgenii Shevchenko
|
5
|
+
# @!attribute [r] graph
|
6
|
+
# @return [NMatrix] graph map matrix
|
7
|
+
# @!attribute [r] queue
|
8
|
+
# @return [Queue] progress queue
|
9
|
+
# @!attribute [r] rows
|
10
|
+
# @return [Integer] number of the rows
|
11
|
+
# @!attribute [r] cols
|
12
|
+
# @return [Integer] number of the cols
|
13
|
+
class Graph
|
14
|
+
attr_reader :graph
|
15
|
+
attr_reader :queue
|
16
|
+
attr_reader :rows
|
17
|
+
attr_reader :cols
|
18
|
+
|
19
|
+
INF = Float::INFINITY
|
20
|
+
BLANK = -Float::INFINITY
|
21
|
+
VISITED = Float::MAX
|
22
|
+
START = 1
|
23
|
+
FINISH = 2
|
24
|
+
|
25
|
+
# Initializes Graph object, sets rows and cols, graph map
|
26
|
+
#
|
27
|
+
# @param n [Integer] number of rows
|
28
|
+
# @param m [Integer] number of columns
|
29
|
+
# @param graph_map [Array] prepared vector of the graph map
|
30
|
+
def initialize(n, m, graph_map)
|
31
|
+
@rows, @cols = n, m
|
32
|
+
@graph = NMatrix.new([n, m], graph_map)
|
33
|
+
@queue = Queue.new
|
34
|
+
end
|
35
|
+
|
36
|
+
# Starts a search for the shortest path
|
37
|
+
#
|
38
|
+
# @param start [Array] start vertex coords
|
39
|
+
# @param finish [Array] finish vertex coords
|
40
|
+
def shortest_path(start, finish)
|
41
|
+
queue << [start, 0]
|
42
|
+
loop do
|
43
|
+
break if queue.empty?
|
44
|
+
vertex, d = queue.pop
|
45
|
+
graph[*vertex] = d
|
46
|
+
break if vertex == finish
|
47
|
+
enqueue_neighbours(*vertex, d + 1)
|
48
|
+
end
|
49
|
+
queue.clear
|
50
|
+
!blank?(finish) ? build_path(start, finish) : []
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Checks if vertex is blank
|
56
|
+
#
|
57
|
+
# @param vertex [Array] vertex coords
|
58
|
+
def blank?(vertex)
|
59
|
+
graph[*vertex] == BLANK
|
60
|
+
end
|
61
|
+
|
62
|
+
# Builds the shortest path from the processed graph
|
63
|
+
#
|
64
|
+
# @param start [Array] start vertex coords
|
65
|
+
# @param finish [Array] finish vertex coords
|
66
|
+
def build_path(start, finish)
|
67
|
+
path = [finish]
|
68
|
+
loop do
|
69
|
+
vertex = path.last
|
70
|
+
d = graph[*vertex]
|
71
|
+
neighbours = get_neighbours(*vertex)
|
72
|
+
next_vertex = neighbours.select{|n_vert| graph[*n_vert] == d - 1}.first
|
73
|
+
path << next_vertex if next_vertex
|
74
|
+
break if vertex == start
|
75
|
+
end
|
76
|
+
path
|
77
|
+
end
|
78
|
+
|
79
|
+
# Collects the neighbours of the given vertex
|
80
|
+
#
|
81
|
+
# @param n [Integer] row number of vertex
|
82
|
+
# @param m [Integer] column number of vertex
|
83
|
+
def get_neighbours(n, m)
|
84
|
+
[[n - 1, m],[n + 1, m],[n, m - 1],[n, m + 1]].select do |coords|
|
85
|
+
coords.first.between?(0, rows - 1) && coords.last.between?(0, cols - 1)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Adds the neighbours of the given vertex to the queue and marks them
|
90
|
+
#
|
91
|
+
# @param n [Integer] row number of vertex
|
92
|
+
# @param m [Integer] column number of vertex
|
93
|
+
# @param d [Integer] distance mark of vertex
|
94
|
+
def enqueue_neighbours(n, m, d)
|
95
|
+
get_neighbours(n, m).each do |vertex|
|
96
|
+
if blank?(vertex)
|
97
|
+
graph[*vertex] = VISITED
|
98
|
+
queue << [vertex, d]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Mazemap
|
2
|
+
# Pathfinder object class
|
3
|
+
#
|
4
|
+
# @author Evgenii Shevchenko
|
5
|
+
# @!attribute [r] lines
|
6
|
+
# @return [Array] maze representation
|
7
|
+
# @!attribute [r] rows
|
8
|
+
# @return [Integer] number of the rows
|
9
|
+
# @!attribute [r] cols
|
10
|
+
# @return [Integer] number of the cols
|
11
|
+
# @!attribute [r] start
|
12
|
+
# @return [Integer] start vertex coords
|
13
|
+
# @!attribute [r] finish
|
14
|
+
# @return [Integer] finish vertex coords
|
15
|
+
class Pathfinder
|
16
|
+
attr_reader :lines
|
17
|
+
attr_reader :rows
|
18
|
+
attr_reader :cols
|
19
|
+
attr_reader :start
|
20
|
+
attr_reader :finish
|
21
|
+
|
22
|
+
VERTEX_VALUES = {
|
23
|
+
' ' => Graph::BLANK,
|
24
|
+
'*' => Graph::INF,
|
25
|
+
'A' => Graph::START,
|
26
|
+
'B' => Graph::FINISH
|
27
|
+
}
|
28
|
+
|
29
|
+
# Initializes Pathfinder, sets @lines, @rows and @cols
|
30
|
+
#
|
31
|
+
# @param filename [String] path to file with maze
|
32
|
+
def initialize(filename)
|
33
|
+
if filename
|
34
|
+
@lines = IO.readlines(filename).map(&:chomp)
|
35
|
+
@rows = lines.length
|
36
|
+
@cols = lines.first.length
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Starts a search for the shortest path
|
41
|
+
def solution
|
42
|
+
graph_map = parse_graph_map.flatten
|
43
|
+
set_route(graph_map)
|
44
|
+
graph = Graph.new(rows, cols, graph_map)
|
45
|
+
graph.shortest_path(start, finish).map(&:reverse).reverse
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
# Sets start and finish vertex coords
|
51
|
+
#
|
52
|
+
# @param graph_map [Array] flatterned graph map
|
53
|
+
def set_route(graph_map)
|
54
|
+
start_element = graph_map.index(Graph::START)
|
55
|
+
finish_element = graph_map.index(Graph::FINISH)
|
56
|
+
graph_map[start_element] = Graph::BLANK
|
57
|
+
graph_map[finish_element] = Graph::BLANK
|
58
|
+
@start = [start_element / cols, start_element % cols]
|
59
|
+
@finish = [finish_element / cols, finish_element % cols]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Converts letter according to maze format
|
63
|
+
#
|
64
|
+
# @param letter [String] letter to convert
|
65
|
+
def convert_letter(letter)
|
66
|
+
VERTEX_VALUES[letter] || letter
|
67
|
+
end
|
68
|
+
|
69
|
+
# Parses the maze map and converts it to the internal format
|
70
|
+
def parse_graph_map
|
71
|
+
lines.map do |line|
|
72
|
+
line.split('').map do |letter|
|
73
|
+
convert_letter(letter)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/mazemap.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mazemap/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.authors = ["Evgenii Shevchenko"]
|
8
|
+
|
9
|
+
spec.name = 'mazemap'
|
10
|
+
spec.version = '0.0.1'
|
11
|
+
spec.date = '2016-12-16'
|
12
|
+
spec.summary = 'MazeMap Graph Pathfinder'
|
13
|
+
spec.description = 'Runs Wavefront algorithm to find the shortest path from A to B'
|
14
|
+
spec.homepage = 'https://github.com/leafmind/mazemap'
|
15
|
+
|
16
|
+
spec.email = 'leafmind@gmail.com'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
19
|
+
f.match(%r{^(test|spec|features)/})
|
20
|
+
end
|
21
|
+
|
22
|
+
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
spec.version = Mazemap::VERSION
|
26
|
+
|
27
|
+
spec.license = 'MIT'
|
28
|
+
|
29
|
+
spec.add_dependency 'nmatrix'
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
32
|
+
spec.add_development_dependency "rake", "~> 11.3"
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.5"
|
34
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
35
|
+
spec.add_development_dependency "shoulda-matchers"
|
36
|
+
spec.add_development_dependency "awesome_print"
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mazemap
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Evgenii Shevchenko
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-12-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nmatrix
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.13'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.13'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '11.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '11.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.5'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.5'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.9'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.9'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: shoulda-matchers
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: awesome_print
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Runs Wavefront algorithm to find the shortest path from A to B
|
112
|
+
email: leafmind@gmail.com
|
113
|
+
executables:
|
114
|
+
- mazemap
|
115
|
+
- setup
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- ".gitignore"
|
120
|
+
- ".rpsec"
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- bin/mazemap
|
126
|
+
- bin/setup
|
127
|
+
- lib/mazemap.rb
|
128
|
+
- lib/mazemap/graph.rb
|
129
|
+
- lib/mazemap/pathfinder.rb
|
130
|
+
- lib/mazemap/version.rb
|
131
|
+
- mazemap.gemspec
|
132
|
+
homepage: https://github.com/leafmind/mazemap
|
133
|
+
licenses:
|
134
|
+
- MIT
|
135
|
+
metadata: {}
|
136
|
+
post_install_message:
|
137
|
+
rdoc_options: []
|
138
|
+
require_paths:
|
139
|
+
- lib
|
140
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
requirements: []
|
151
|
+
rubyforge_project:
|
152
|
+
rubygems_version: 2.5.2
|
153
|
+
signing_key:
|
154
|
+
specification_version: 4
|
155
|
+
summary: MazeMap Graph Pathfinder
|
156
|
+
test_files: []
|