mvgraph 0.0.1
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/Gemfile +0 -0
- data/Gemfile.lock +10 -0
- data/MIT-LICENSE.txt +19 -0
- data/README +2 -0
- data/Rakefile +26 -0
- data/doc/design.txt +42 -0
- data/lib/mvGraph.rb +202 -0
- data/lib/mvQueue.rb +29 -0
- data/test/test_mvGraph.rb +183 -0
- data/test/test_mvQueue.rb +46 -0
- metadata +62 -0
data/Gemfile
ADDED
|
File without changes
|
data/Gemfile.lock
ADDED
data/MIT-LICENSE.txt
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2011 Daniel Vingo, Jonathan McKenzie
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
data/README
ADDED
data/Rakefile
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'rake/gempackagetask'
|
|
2
|
+
|
|
3
|
+
spec = Gem::Specification.new do |s|
|
|
4
|
+
s.name = "mvgraph"
|
|
5
|
+
s.summary = "A generic graph implementation, useful for solving problems where some sort of search is needed."
|
|
6
|
+
s.description = File.read(File.join(File.dirname(__FILE__), 'README'))
|
|
7
|
+
s.requirements = [ 'None' ]
|
|
8
|
+
s.version = "0.0.1"
|
|
9
|
+
s.author = "Jon Mckenzie, Dan Vingo"
|
|
10
|
+
s.email = "danvingo@gmail.com"
|
|
11
|
+
s.homepage = ""
|
|
12
|
+
s.platform = Gem::Platform::RUBY
|
|
13
|
+
s.required_ruby_version = '>=1.9.2p180'
|
|
14
|
+
s.files = Dir['**/**']
|
|
15
|
+
s.executables = []
|
|
16
|
+
s.test_files = Dir["test/test*.rb"]
|
|
17
|
+
s.has_rdoc = false
|
|
18
|
+
end
|
|
19
|
+
Rake::GemPackageTask.new(spec).define
|
|
20
|
+
|
|
21
|
+
task :default => [:test]
|
|
22
|
+
|
|
23
|
+
task :test do
|
|
24
|
+
ruby "test/test_mvQueue.rb"
|
|
25
|
+
ruby "test/test_mvGraph.rb"
|
|
26
|
+
end
|
data/doc/design.txt
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
The graph is currently implemented in an undirected manner, such that
|
|
2
|
+
adding an edge will add edges in both directions, this can easily be changed
|
|
3
|
+
for example to implement a directed graph, or if you desire the behavior to
|
|
4
|
+
explicitly set edges in both directions for two vertices.
|
|
5
|
+
==============
|
|
6
|
+
New Features
|
|
7
|
+
==============
|
|
8
|
+
-Graph Visualization
|
|
9
|
+
-Output
|
|
10
|
+
-ASCII
|
|
11
|
+
-JSON
|
|
12
|
+
-OpenGL/WebGL
|
|
13
|
+
-Static image (png, jpeg, etc.)
|
|
14
|
+
-Graph Methods
|
|
15
|
+
-Search
|
|
16
|
+
*-Breadth-first
|
|
17
|
+
*-Shortest-path
|
|
18
|
+
*-Depth-first
|
|
19
|
+
-Iterative Depth-first
|
|
20
|
+
*-Best-first
|
|
21
|
+
-Greedy
|
|
22
|
+
-A*
|
|
23
|
+
-Database
|
|
24
|
+
-Back-end representation
|
|
25
|
+
-Give the option to use adjacency list or adjacency matrix to use more memory but make the search faster
|
|
26
|
+
-Sub/Supertypes
|
|
27
|
+
-Simple
|
|
28
|
+
-Directed
|
|
29
|
+
-Weighted-edges
|
|
30
|
+
-Hypergraph
|
|
31
|
+
-Toy implementations
|
|
32
|
+
-Slide puzzle
|
|
33
|
+
-Chess/checkers
|
|
34
|
+
-Perfomance enhancements
|
|
35
|
+
-Hash function to compare states instead of nxn linear search of the grid size when testing equality.
|
|
36
|
+
|
|
37
|
+
======
|
|
38
|
+
TODO
|
|
39
|
+
======
|
|
40
|
+
- Add a README
|
|
41
|
+
- Setup Rake tasks for testing with nPuzzle and mvGraph together.
|
|
42
|
+
|
data/lib/mvGraph.rb
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
require_relative 'mvQueue.rb'
|
|
2
|
+
class Graph
|
|
3
|
+
include Enumerable
|
|
4
|
+
def each
|
|
5
|
+
@adj_matrix.keys.each do |vertex|
|
|
6
|
+
yield vertex
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def neighbors_of(vertex)
|
|
11
|
+
@adj_matrix[vertex].select { |v| has_edge?(vertex, v) }.keys
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@adj_matrix = Hash.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add_vertex(v)
|
|
19
|
+
@adj_matrix[v] = Hash.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def vertex(v)
|
|
23
|
+
@adj_matrix.find { |vertex| vertex[0] == v }[0]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_edge(v1, v2)
|
|
27
|
+
if @adj_matrix[v1].nil?
|
|
28
|
+
@adj_matrix[v1] = Hash.new
|
|
29
|
+
end
|
|
30
|
+
if @adj_matrix[v2].nil?
|
|
31
|
+
@adj_matrix[v2] = Hash.new
|
|
32
|
+
end
|
|
33
|
+
@adj_matrix[v2][v1] = 1
|
|
34
|
+
@adj_matrix[v1][v2] = 1
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def update_vertex(v)
|
|
38
|
+
@adj_matrix[v] = @adj_matrix[vertex(v)]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def has_edge?(v1, v2)
|
|
42
|
+
unless @adj_matrix[v1].nil? or @adj_matrix[v1][v2].nil?
|
|
43
|
+
@adj_matrix[v1][v2] == 1
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# queue_type is one of fifo or lifo
|
|
49
|
+
#
|
|
50
|
+
# Look into passing a hash of arguments
|
|
51
|
+
def search(start_vertex, queue_type, neighbor_function=nil, goal_state=nil, search_depth=nil)
|
|
52
|
+
set_vertex_color(start_vertex, "gray")
|
|
53
|
+
set_vertex_distance(start_vertex, 0)
|
|
54
|
+
queue = Queue.new(queue_type)
|
|
55
|
+
queue.enqueue(get_graph_vertex(start_vertex))
|
|
56
|
+
goal_not_reached = true
|
|
57
|
+
count = 0
|
|
58
|
+
return_goal_state = nil
|
|
59
|
+
while queue.length != 0 and goal_not_reached
|
|
60
|
+
current_vertex = queue.dequeue
|
|
61
|
+
count += 1
|
|
62
|
+
# Full BFS or DFS with no goal state
|
|
63
|
+
if neighbor_function.nil? and search_depth.nil? and goal_state.nil?
|
|
64
|
+
neighbors_of(current_vertex).each do |neighbor|
|
|
65
|
+
if neighbor.color == "white"
|
|
66
|
+
set_vertex_color(get_graph_vertex(neighbor), "gray")
|
|
67
|
+
set_vertex_distance(get_graph_vertex(neighbor), get_vertex_distance(get_graph_vertex(current_vertex)) + 1)
|
|
68
|
+
set_vertex_predecessor(get_graph_vertex(neighbor), get_graph_vertex(current_vertex))
|
|
69
|
+
queue.enqueue(get_graph_vertex(neighbor))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
set_vertex_color(get_graph_vertex(current_vertex), "black")
|
|
73
|
+
# Search for goal_state with no distance limit
|
|
74
|
+
elsif !neighbor_function.nil? and !goal_state.nil? and search_depth.nil?
|
|
75
|
+
neighbors = get_graph_vertex(current_vertex).send(neighbor_function, goal_state)
|
|
76
|
+
neighbors.each do |neighbor|
|
|
77
|
+
unless self.include? neighbor
|
|
78
|
+
self.add_vertex(neighbor)
|
|
79
|
+
self.add_edge(current_vertex, neighbor)
|
|
80
|
+
if neighbor == goal_state
|
|
81
|
+
set_vertex_color(get_graph_vertex(neighbor), "gray")
|
|
82
|
+
set_vertex_distance(get_graph_vertex(neighbor), get_vertex_distance(get_graph_vertex(current_vertex)) + 1)
|
|
83
|
+
set_vertex_predecessor(get_graph_vertex(neighbor), get_graph_vertex(current_vertex))
|
|
84
|
+
puts " a;dslfkj #{return_goal_state} "
|
|
85
|
+
return_goal_state = neighbor
|
|
86
|
+
goal_not_reached = false
|
|
87
|
+
break
|
|
88
|
+
end
|
|
89
|
+
if get_vertex_color(get_graph_vertex(neighbor)) == "white"
|
|
90
|
+
set_vertex_color(get_graph_vertex(neighbor), "gray")
|
|
91
|
+
set_vertex_distance(get_graph_vertex(neighbor), get_vertex_distance(get_graph_vertex(current_vertex)) + 1)
|
|
92
|
+
set_vertex_predecessor(get_graph_vertex(neighbor), get_graph_vertex(current_vertex))
|
|
93
|
+
queue.enqueue(get_graph_vertex(neighbor))
|
|
94
|
+
end
|
|
95
|
+
set_vertex_color(get_graph_vertex(current_vertex), "black")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
# Random walk to a given distance and no goal state
|
|
99
|
+
elsif !neighbor_function.nil? and goal_state.nil? and !search_depth.nil?
|
|
100
|
+
neighbors = get_graph_vertex(current_vertex).send(neighbor_function)
|
|
101
|
+
neighbors.each do |neighbor|
|
|
102
|
+
unless self.include? neighbor
|
|
103
|
+
self.add_vertex(neighbor)
|
|
104
|
+
self.add_edge(current_vertex, neighbor)
|
|
105
|
+
if count == search_depth
|
|
106
|
+
return_goal_state = neighbor
|
|
107
|
+
goal_not_reached = false
|
|
108
|
+
break
|
|
109
|
+
end
|
|
110
|
+
if get_vertex_color(get_graph_vertex(neighbor)) == "white"
|
|
111
|
+
set_vertex_color(get_graph_vertex(neighbor), "gray")
|
|
112
|
+
set_vertex_distance(get_graph_vertex(neighbor), get_vertex_distance(get_graph_vertex(current_vertex)) + 1)
|
|
113
|
+
set_vertex_predecessor(get_graph_vertex(neighbor), get_graph_vertex(current_vertex))
|
|
114
|
+
queue.enqueue(get_graph_vertex(neighbor))
|
|
115
|
+
end
|
|
116
|
+
set_vertex_color(get_graph_vertex(current_vertex), "black")
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
else
|
|
120
|
+
# Other possible prototypes
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
puts "return_goal_state: #{return_goal_state}"
|
|
124
|
+
return return_goal_state, count
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Performs depth-first search to the given depth, starting at the vertex start_state.
|
|
128
|
+
def walk_n_steps(start_state, neighbor_function, depth)
|
|
129
|
+
search(start_state, "lifo", neighbor_function, nil, depth)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# Precondition: BFS has been run on the graph
|
|
134
|
+
#
|
|
135
|
+
def shortest_path(start_vertex, end_vertex)
|
|
136
|
+
@ret_array ||= []
|
|
137
|
+
predecessor = get_vertex_predecessor(get_graph_vertex(end_vertex))
|
|
138
|
+
if start_vertex == end_vertex
|
|
139
|
+
@ret_array << get_graph_vertex(start_vertex)
|
|
140
|
+
elsif predecessor.nil?
|
|
141
|
+
"No path."
|
|
142
|
+
else
|
|
143
|
+
shortest_path(get_graph_vertex(start_vertex), predecessor)
|
|
144
|
+
@ret_array << get_graph_vertex(end_vertex)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def set_vertex_color(v, color)
|
|
149
|
+
get_graph_vertex(v).color = color
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def get_vertex_color(v)
|
|
153
|
+
get_graph_vertex(v).color
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def set_vertex_distance(v, distance)
|
|
157
|
+
get_graph_vertex(v).distance = distance
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def get_vertex_distance(v)
|
|
161
|
+
get_graph_vertex(v).distance
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def set_vertex_predecessor(v, predecessor)
|
|
165
|
+
get_graph_vertex(v).predecessor = get_graph_vertex(predecessor) unless predecessor.nil?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def get_vertex_predecessor(v)
|
|
169
|
+
get_graph_vertex(v).predecessor
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
private
|
|
173
|
+
def get_graph_vertex(v)
|
|
174
|
+
@adj_matrix.find { |vertex| vertex[0] == v }[0]
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
class Vertex
|
|
179
|
+
attr_accessor :id, :color, :distance, :predecessor
|
|
180
|
+
def initialize(id)
|
|
181
|
+
@id = id
|
|
182
|
+
@color = "white"
|
|
183
|
+
@distance = 2**32
|
|
184
|
+
@predecessor = nil
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def ==(other_vertex)
|
|
188
|
+
@id == other_vertex.id
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def eql?(other_vertex)
|
|
192
|
+
@id == other_vertex.id
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def hash
|
|
196
|
+
@id.hash
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def to_s
|
|
200
|
+
"id: #{@id}, color: #{@color}, distance: #{@distance}, predecessor: \n\t\t #{@predecessor}"
|
|
201
|
+
end
|
|
202
|
+
end
|
data/lib/mvQueue.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class Queue < Array
|
|
2
|
+
def initialize(type)
|
|
3
|
+
super()
|
|
4
|
+
@type = type
|
|
5
|
+
@func_map = {"fi" => Proc.new{ |item| self.insert(0, item) }, "li" => Proc.new{ |item| self << item }, "fo" => Proc.new{ self.pop }, "lo" => Proc.new{ delete_at(0) },
|
|
6
|
+
"be" => Proc.new do |item|
|
|
7
|
+
self.insert(0, item)
|
|
8
|
+
self.sort_by! { |state| state.score }
|
|
9
|
+
end
|
|
10
|
+
}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def enqueue(item)
|
|
14
|
+
@func_map[@type[0..1]].call(item)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def dequeue
|
|
18
|
+
@func_map[@type[2..3]].call()
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
the_string = ""
|
|
23
|
+
self.each do |item|
|
|
24
|
+
the_string += item.to_s
|
|
25
|
+
the_string += ","
|
|
26
|
+
end
|
|
27
|
+
the_string[0..the_string.size()-2]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require_relative '../lib/mvGraph.rb'
|
|
3
|
+
|
|
4
|
+
class TestGraph < MiniTest::Unit::TestCase
|
|
5
|
+
def setup
|
|
6
|
+
@graph = Graph.new
|
|
7
|
+
@vertex = Vertex.new(1)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def test_create_graph
|
|
11
|
+
assert @graph
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_create_vertex
|
|
15
|
+
assert @vertex
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_vertex_id
|
|
19
|
+
@vertex.id = 1
|
|
20
|
+
assert @vertex.id == 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_add_vertex
|
|
24
|
+
@graph.add_vertex(Vertex.new(1))
|
|
25
|
+
assert @graph.vertex(Vertex.new(1))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_add_edge
|
|
29
|
+
vertex1 = Vertex.new(1)
|
|
30
|
+
@graph.add_vertex(vertex1)
|
|
31
|
+
@graph.add_vertex(Vertex.new(2))
|
|
32
|
+
@graph.add_edge(vertex1, Vertex.new(2))
|
|
33
|
+
assert @graph.has_edge?(Vertex.new(1), Vertex.new(2))
|
|
34
|
+
assert @graph.has_edge?(Vertex.new(2), Vertex.new(1))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_no_edge
|
|
38
|
+
refute @graph.has_edge?(Vertex.new(3),Vertex.new(4))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_add_multiple_vertices_and_edges
|
|
42
|
+
@graph.add_vertex(Vertex.new(1))
|
|
43
|
+
@graph.add_vertex(Vertex.new(2))
|
|
44
|
+
@graph.add_edge(Vertex.new(2),Vertex.new(1))
|
|
45
|
+
@graph.add_vertex(Vertex.new(3))
|
|
46
|
+
@graph.add_vertex(Vertex.new(4))
|
|
47
|
+
@graph.add_edge(Vertex.new(3),Vertex.new(4))
|
|
48
|
+
@graph.add_edge(Vertex.new(1),Vertex.new(4))
|
|
49
|
+
@graph.add_edge(Vertex.new(3),Vertex.new(2))
|
|
50
|
+
assert @graph.has_edge?(Vertex.new(1),Vertex.new(2))
|
|
51
|
+
assert @graph.has_edge?(Vertex.new(3),Vertex.new(4))
|
|
52
|
+
assert @graph.has_edge?(Vertex.new(4),Vertex.new(1))
|
|
53
|
+
assert @graph.has_edge?(Vertex.new(3),Vertex.new(2))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def test_loop
|
|
57
|
+
@graph.add_vertex(Vertex.new(1))
|
|
58
|
+
@graph.add_edge(Vertex.new(1),Vertex.new(1))
|
|
59
|
+
assert @graph.has_edge?(Vertex.new(1),Vertex.new(1)), "@graph should have the edge just added."
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_vertex_equal
|
|
63
|
+
assert Vertex.new(1).eql? Vertex.new(1)
|
|
64
|
+
assert Vertex.new("12345").eql? Vertex.new("12345")
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def bfs_setup_hard
|
|
68
|
+
@bfs_graph = Graph.new
|
|
69
|
+
@bfs_graph.add_vertex(Vertex.new(1))
|
|
70
|
+
@bfs_graph.add_vertex(Vertex.new(2))
|
|
71
|
+
@bfs_graph.add_vertex(Vertex.new(3))
|
|
72
|
+
@bfs_graph.add_vertex(Vertex.new(4))
|
|
73
|
+
@bfs_graph.add_vertex(Vertex.new(5))
|
|
74
|
+
@bfs_graph.add_vertex(Vertex.new(6))
|
|
75
|
+
@bfs_graph.add_vertex(Vertex.new(7))
|
|
76
|
+
@bfs_graph.add_vertex(Vertex.new(8))
|
|
77
|
+
@bfs_graph.add_vertex(Vertex.new(9))
|
|
78
|
+
@bfs_graph.add_vertex(Vertex.new(10))
|
|
79
|
+
@bfs_graph.add_vertex(Vertex.new(11))
|
|
80
|
+
|
|
81
|
+
@bfs_graph.add_edge(Vertex.new(1),Vertex.new(2))
|
|
82
|
+
@bfs_graph.add_edge(Vertex.new(1),Vertex.new(3))
|
|
83
|
+
@bfs_graph.add_edge(Vertex.new(1),Vertex.new(4))
|
|
84
|
+
@bfs_graph.add_edge(Vertex.new(2),Vertex.new(5))
|
|
85
|
+
@bfs_graph.add_edge(Vertex.new(5),Vertex.new(6))
|
|
86
|
+
@bfs_graph.add_edge(Vertex.new(6),Vertex.new(7))
|
|
87
|
+
@bfs_graph.add_edge(Vertex.new(7),Vertex.new(8))
|
|
88
|
+
@bfs_graph.add_edge(Vertex.new(11),Vertex.new(8))
|
|
89
|
+
@bfs_graph.add_edge(Vertex.new(10),Vertex.new(5))
|
|
90
|
+
@bfs_graph.add_edge(Vertex.new(10),Vertex.new(9))
|
|
91
|
+
@bfs_graph.add_edge(Vertex.new(11),Vertex.new(9))
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def bfs_setup_easy
|
|
95
|
+
@bfs_graph = Graph.new
|
|
96
|
+
@bfs_graph.add_vertex(Vertex.new(1))
|
|
97
|
+
@bfs_graph.add_vertex(Vertex.new(2))
|
|
98
|
+
@bfs_graph.add_vertex(Vertex.new(3))
|
|
99
|
+
@bfs_graph.add_vertex(Vertex.new(4))
|
|
100
|
+
@bfs_graph.add_edge(Vertex.new(1), Vertex.new(2))
|
|
101
|
+
@bfs_graph.add_edge(Vertex.new(1), Vertex.new(3))
|
|
102
|
+
@bfs_graph.add_edge(Vertex.new(1), Vertex.new(4))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
#def test_bfs
|
|
106
|
+
# bfs_setup_hard
|
|
107
|
+
# assert @bfs_graph
|
|
108
|
+
# @bfs_graph.search(Vertex.new(1), "fifo")
|
|
109
|
+
# assert_equal "black", @bfs_graph.vertex(Vertex.new(1)).color
|
|
110
|
+
# assert_equal 0, @bfs_graph.get_vertex_distance(Vertex.new(1))
|
|
111
|
+
# assert_equal nil, @bfs_graph.get_vertex_predecessor(Vertex.new(1))
|
|
112
|
+
# assert_equal @bfs_graph.get_vertex_color(Vertex.new(11)), "black"
|
|
113
|
+
# assert @bfs_graph.get_vertex_distance(Vertex.new(11)) == 5
|
|
114
|
+
# assert @bfs_graph.get_vertex_predecessor(Vertex.new(11)) == Vertex.new(9)
|
|
115
|
+
#end
|
|
116
|
+
|
|
117
|
+
#def test_shortest_path
|
|
118
|
+
# bfs_setup_hard
|
|
119
|
+
# assert @bfs_graph
|
|
120
|
+
# @bfs_graph.search(Vertex.new(1), "fifo")
|
|
121
|
+
# path = @bfs_graph.shortest_path(Vertex.new(1), Vertex.new(11))
|
|
122
|
+
# assert path[0] == Vertex.new(1)
|
|
123
|
+
# assert path[1] == Vertex.new(2)
|
|
124
|
+
# assert path[2] == Vertex.new(5)
|
|
125
|
+
# assert path[3] == Vertex.new(10)
|
|
126
|
+
# assert path[4] == Vertex.new(9)
|
|
127
|
+
# assert path[5] == Vertex.new(11)
|
|
128
|
+
#end
|
|
129
|
+
|
|
130
|
+
#def test_no_shortest_path
|
|
131
|
+
# bfs_setup_hard
|
|
132
|
+
# assert @bfs_graph
|
|
133
|
+
#@bfs_graph.add_vertex(Vertex.new(20))
|
|
134
|
+
#@bfs_graph.search(Vertex.new(1), "fifo")
|
|
135
|
+
#path = @bfs_graph.shortest_path(Vertex.new(1), Vertex.new(20))
|
|
136
|
+
#assert path == "No path."
|
|
137
|
+
#end
|
|
138
|
+
|
|
139
|
+
def test_get_all_vertices
|
|
140
|
+
bfs_setup_easy
|
|
141
|
+
@bfs_graph.each { |vertex| assert vertex }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def test_neighbors_of
|
|
145
|
+
bfs_setup_easy
|
|
146
|
+
neighbors = @bfs_graph.neighbors_of(Vertex.new(1))
|
|
147
|
+
assert_equal 2, neighbors[0].id, "First neighbor should be 2."
|
|
148
|
+
assert_equal 3, neighbors[1].id, "Second neighbor should be 3."
|
|
149
|
+
assert_equal 4, neighbors[2].id, "Third neighbor should be 4."
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def test_vertex_color
|
|
153
|
+
v = Vertex.new(1)
|
|
154
|
+
v.color = "gray"
|
|
155
|
+
@graph.add_vertex(v)
|
|
156
|
+
assert_equal "gray", @graph.vertex(Vertex.new(1)).color
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def test_vertex_distance
|
|
160
|
+
v = Vertex.new(1)
|
|
161
|
+
v.distance = 100
|
|
162
|
+
@graph.add_vertex(v)
|
|
163
|
+
assert_equal 100, @graph.vertex(Vertex.new(1)).distance
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def test_vertex_predecessor
|
|
167
|
+
@graph.add_vertex(Vertex.new(1))
|
|
168
|
+
@graph.vertex(Vertex.new(1)).predecessor = "5"
|
|
169
|
+
assert_equal "5", @graph.vertex(Vertex.new(1)).predecessor
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
#def test_dfs
|
|
173
|
+
# bfs_setup_hard
|
|
174
|
+
# assert @bfs_graph
|
|
175
|
+
# @bfs_graph.search(Vertex.new(1), "lifo")
|
|
176
|
+
# assert_equal "black", @bfs_graph.vertex(Vertex.new(1)).color
|
|
177
|
+
# assert_equal 0, @bfs_graph.vertex(Vertex.new(1)).distance
|
|
178
|
+
# assert_equal nil, @bfs_graph.vertex(Vertex.new(1)).predecessor
|
|
179
|
+
# assert_equal @bfs_graph.vertex(Vertex.new(11)).color, "black"
|
|
180
|
+
# assert @bfs_graph.vertex(Vertex.new(11)).distance == 5
|
|
181
|
+
# assert @bfs_graph.vertex(Vertex.new(11)).predecessor == Vertex.new(9)
|
|
182
|
+
#end
|
|
183
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
require_relative '../lib/mvQueue.rb'
|
|
3
|
+
|
|
4
|
+
class TestQueue < MiniTest::Unit::TestCase
|
|
5
|
+
def setup
|
|
6
|
+
@queue = Queue.new("fifo")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def iterate_setup(type)
|
|
10
|
+
@queue = Queue.new(type)
|
|
11
|
+
@queue.enqueue(1)
|
|
12
|
+
@queue.enqueue(2)
|
|
13
|
+
@queue.enqueue(3)
|
|
14
|
+
@queue.enqueue(4)
|
|
15
|
+
@queue.enqueue(5)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_enqueue_dequeue
|
|
19
|
+
@queue.enqueue("x")
|
|
20
|
+
assert_equal @queue.dequeue, "x"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_queue_accessor_is_private
|
|
24
|
+
refute @queue.respond_to? :queue
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_iterate_queue
|
|
28
|
+
iterate_setup("fifo")
|
|
29
|
+
assert_equal 1, @queue.dequeue
|
|
30
|
+
assert_equal 2, @queue.dequeue
|
|
31
|
+
assert_equal 3, @queue.dequeue
|
|
32
|
+
assert_equal 4, @queue.dequeue
|
|
33
|
+
assert_equal 5, @queue.dequeue
|
|
34
|
+
assert @queue.empty?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_to_string
|
|
38
|
+
iterate_setup("fifo")
|
|
39
|
+
assert_equal "5,4,3,2,1", @queue.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_to_string_stack
|
|
43
|
+
iterate_setup("lifo")
|
|
44
|
+
assert_equal "1,2,3,4,5", @queue.to_s
|
|
45
|
+
end
|
|
46
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mvgraph
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Jon Mckenzie, Dan Vingo
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2011-05-17 00:00:00.000000000 -04:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies: []
|
|
15
|
+
description: ! "mvGraph is an implementation of the graph data structure in Ruby.
|
|
16
|
+
\ It was created to solve \nsome classic AI search problems; as such, it currently
|
|
17
|
+
supports BFS, DFS, any user-defined heuristic search, as well as A*.\n"
|
|
18
|
+
email: danvingo@gmail.com
|
|
19
|
+
executables: []
|
|
20
|
+
extensions: []
|
|
21
|
+
extra_rdoc_files: []
|
|
22
|
+
files:
|
|
23
|
+
- MIT-LICENSE.txt
|
|
24
|
+
- test/test_mvGraph.rb
|
|
25
|
+
- test/test_mvQueue.rb
|
|
26
|
+
- Gemfile.lock
|
|
27
|
+
- Gemfile
|
|
28
|
+
- README
|
|
29
|
+
- doc/design.txt
|
|
30
|
+
- lib/mvQueue.rb
|
|
31
|
+
- lib/mvGraph.rb
|
|
32
|
+
- Rakefile
|
|
33
|
+
has_rdoc: true
|
|
34
|
+
homepage: ''
|
|
35
|
+
licenses: []
|
|
36
|
+
post_install_message:
|
|
37
|
+
rdoc_options: []
|
|
38
|
+
require_paths:
|
|
39
|
+
- lib
|
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 1.9.2p180
|
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
47
|
+
none: false
|
|
48
|
+
requirements:
|
|
49
|
+
- - ! '>='
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '0'
|
|
52
|
+
requirements:
|
|
53
|
+
- None
|
|
54
|
+
rubyforge_project:
|
|
55
|
+
rubygems_version: 1.6.2
|
|
56
|
+
signing_key:
|
|
57
|
+
specification_version: 3
|
|
58
|
+
summary: A generic graph implementation, useful for solving problems where some sort
|
|
59
|
+
of search is needed.
|
|
60
|
+
test_files:
|
|
61
|
+
- test/test_mvGraph.rb
|
|
62
|
+
- test/test_mvQueue.rb
|