Moby 0.5.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/Examples/Dijkstra/CalculateShortestDistance.rb +65 -0
- data/Examples/Dijkstra/calculate_shortest_path.rb +352 -0
- data/Examples/Dijkstra/data.rb +210 -0
- data/Examples/Dijkstra/dijkstra.rb +84 -0
- data/Examples/MoneyTransfer.rb +61 -0
- data/Examples/greeter.rb +24 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Moby.gemspec +28 -0
- data/README.md +41 -0
- data/Rakefile +1 -0
- data/lib/Moby.rb +358 -0
- data/lib/Moby/kernel.rb +5 -0
- data/lib/Moby/version.rb +3 -0
- metadata +158 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
Context::define :CalculateShortestDistance do
|
2
|
+
|
3
|
+
role :tentative_distance_values do end
|
4
|
+
role :path do end
|
5
|
+
|
6
|
+
role :current do end
|
7
|
+
role :destination do end
|
8
|
+
|
9
|
+
|
10
|
+
role :map do
|
11
|
+
distance_between do |a, b|
|
12
|
+
map.distances[Edge.new(a, b)]
|
13
|
+
end
|
14
|
+
|
15
|
+
# These two functions presume always travelling
|
16
|
+
# in a southern or easterly direction
|
17
|
+
next_down_the_street_from do |x|
|
18
|
+
map.east_neighbor_of x
|
19
|
+
end
|
20
|
+
|
21
|
+
next_along_the_avenue_from do |x|
|
22
|
+
map.south_neighbor_of x
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
role :current do
|
27
|
+
tentative_distance do
|
28
|
+
tentative_distance_values[current]
|
29
|
+
end
|
30
|
+
set_tentative_distance_to do |x|
|
31
|
+
tentative_distance_values[current] = x
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
rebind do |origin_node, geometries|
|
37
|
+
@current = origin_node
|
38
|
+
@destination = geometries.destination
|
39
|
+
@map = geometries
|
40
|
+
end
|
41
|
+
|
42
|
+
distance do
|
43
|
+
current.set_tentative_distance_to 0
|
44
|
+
@path = CalculateShortestPath.new(current, destination, map).path
|
45
|
+
retval = 0
|
46
|
+
previous_node = nil
|
47
|
+
path.reverse_each {|node|
|
48
|
+
if previous_node.nil?
|
49
|
+
retval = 0
|
50
|
+
else
|
51
|
+
retval += map.distance_between previous_node, node
|
52
|
+
end
|
53
|
+
previous_node = node
|
54
|
+
}
|
55
|
+
retval
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class CalculateShortestDistance
|
61
|
+
def initialize(origin_node, geometries)
|
62
|
+
rebind(origin_node, geometries)
|
63
|
+
@tentative_distance_values = Hash.new
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,352 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Consider street corners on a Manhattan grid. We want to find the
|
4
|
+
# minimal path from the most northeast city to the most
|
5
|
+
# southeast city. Use Dijstra's algorithm
|
6
|
+
#
|
7
|
+
|
8
|
+
|
9
|
+
#
|
10
|
+
# --------- Contexts: the home of the use cases for the example --------------
|
11
|
+
#
|
12
|
+
|
13
|
+
#
|
14
|
+
# ---------- This is the main Context for shortest path calculation -----------
|
15
|
+
#
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
# There are eight roles in the algorithm:
|
20
|
+
#
|
21
|
+
# pathTo, which is the interface to whatever accumulates the path
|
22
|
+
# current, which is the current intersection in the recursive algorithm
|
23
|
+
# east_neighbor, which lies DIRECTLY to the east of current
|
24
|
+
# south_neighbor, which is DIRECTLy to its south
|
25
|
+
# destination, the target node
|
26
|
+
# map, which is the oracle for the geometry
|
27
|
+
# tentative_distance_values, which supports the algorithm, and is
|
28
|
+
# owned by the CalculateShortestPath context (it is context data)
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# The algorithm is straight from Wikipedia:
|
32
|
+
#
|
33
|
+
# http://en.wikipedia.org/wiki/Dijkstra's_algorithm
|
34
|
+
#
|
35
|
+
# and reads directly from the distance method, below
|
36
|
+
|
37
|
+
|
38
|
+
# "Map" as in cartography rather than Computer Science...
|
39
|
+
#
|
40
|
+
# Map is a DCI role. The role in this example is played by an
|
41
|
+
# object representing a particular Manhattan geometry
|
42
|
+
Context::define :CalculateShortestPath do
|
43
|
+
role :distance_labeled_graph_node do
|
44
|
+
# Access to roles and other Context data
|
45
|
+
tentative_distance_values do
|
46
|
+
tentative_distance_values
|
47
|
+
end
|
48
|
+
# Role Methods
|
49
|
+
tentative_distance do
|
50
|
+
tentative_distance_values[@distance_labeled_graph_node]
|
51
|
+
end
|
52
|
+
set_tentative_distance_to do |x|
|
53
|
+
tentative_distance_values[@distance_labeled_graph_node] = x
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# These are handles to to the roles
|
58
|
+
role :map do
|
59
|
+
distance_between do |a, b|
|
60
|
+
dist = @map.distances[Edge.new(a, b)]
|
61
|
+
# p "distance between #{a.name} and #{b.name} is #{dist}"
|
62
|
+
dist
|
63
|
+
end
|
64
|
+
next_down_the_street_from do |x|
|
65
|
+
n = east_neighbor_of x
|
66
|
+
# p "next down the street from #{x.name} is #{n.name}"
|
67
|
+
n
|
68
|
+
end
|
69
|
+
next_along_the_avenue_from do |x|
|
70
|
+
n = south_neighbor_of x
|
71
|
+
# p "next along the avenue from #{x.name} is #{n.name}"
|
72
|
+
n
|
73
|
+
end
|
74
|
+
origin do
|
75
|
+
map.root
|
76
|
+
end
|
77
|
+
nearest_unvisited_node_to_target do
|
78
|
+
min = infinity
|
79
|
+
selection = nil
|
80
|
+
@unvisited.each_key {
|
81
|
+
|intersection|
|
82
|
+
bind :intersection=>:distance_labeled_graph_node
|
83
|
+
if @unvisited[intersection]
|
84
|
+
tentative_distance = intersection.tentative_distance
|
85
|
+
if tentative_distance < min
|
86
|
+
# p "min distance is updated from #{min} to #{tentative_distance}"
|
87
|
+
min = tentative_distance
|
88
|
+
selection = intersection
|
89
|
+
end
|
90
|
+
end
|
91
|
+
}
|
92
|
+
selection
|
93
|
+
end
|
94
|
+
unvisited do
|
95
|
+
@unvisited
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
role :current do
|
100
|
+
# Access to roles and other Context data
|
101
|
+
unvisited do
|
102
|
+
map.unvisited
|
103
|
+
end
|
104
|
+
|
105
|
+
# Role Methods
|
106
|
+
unvisited_neighbors do
|
107
|
+
retval = Array.new
|
108
|
+
if @south_neighbor != nil
|
109
|
+
if unvisited[@south_neighbor] then retval << @south_neighbor end
|
110
|
+
end
|
111
|
+
if @east_neighbor != nil
|
112
|
+
if unvisited[@east_neighbor] then retval << @east_neighbor end
|
113
|
+
end
|
114
|
+
# p "unvisited neighbors #{retval}"
|
115
|
+
retval
|
116
|
+
end
|
117
|
+
tentative_distance do
|
118
|
+
raise "key (#{current}) not found in #{@tentative_distance_values}" unless @tentative_distance_values && (@tentative_distance_values.has_key? current)
|
119
|
+
@tentative_distance_values[current]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
role :unvisited do end
|
123
|
+
|
124
|
+
|
125
|
+
# This module serves to provide the methods both for the
|
126
|
+
# east_neighbor and south_neighbor roles
|
127
|
+
|
128
|
+
role :neighbor_node do
|
129
|
+
relable_node_as do |x|
|
130
|
+
raise "Argument can't be nil" unless x
|
131
|
+
raise "self can't be nil" unless @neighbor_node
|
132
|
+
|
133
|
+
if x < neighbor_node.tentative_distance
|
134
|
+
# p "updated tentative distance from #{neighbor_node.tentative_distance} to #{x}"
|
135
|
+
neighbor_node.set_tentative_distance_to x
|
136
|
+
:distance_was_udated
|
137
|
+
else
|
138
|
+
# p "left tentative distance at #{neighbor_node.tentative_distance} instead of #{x}"
|
139
|
+
:distance_was_not_udated
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Role Methods
|
144
|
+
tentative_distance do
|
145
|
+
raise "self can't be nil" unless @neighbor_node
|
146
|
+
tentative_distance_values[@neighbor_node]
|
147
|
+
end
|
148
|
+
set_tentative_distance_to do |x|
|
149
|
+
raise "Argument can't be nil" unless x
|
150
|
+
raise "self can't be nil" unless @neighbor_node
|
151
|
+
tentative_distance_values[@neighbor_node] = x
|
152
|
+
end
|
153
|
+
end
|
154
|
+
# This is the method that starts the work. Called from initialize.
|
155
|
+
|
156
|
+
execute do |path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash|
|
157
|
+
do_inits(path_vector, unvisited_hash, pathto_hash,
|
158
|
+
tentative_distance_values_hash)
|
159
|
+
|
160
|
+
|
161
|
+
# Calculate tentative distances of unvisited neighbors
|
162
|
+
|
163
|
+
unvisited_neighbors = current.unvisited_neighbors
|
164
|
+
# p "#{unvisited_neighbors}"
|
165
|
+
if unvisited_neighbors != nil
|
166
|
+
unvisited_neighbors.each {
|
167
|
+
|neighbor|
|
168
|
+
bind :neighbor => :neighbor_node
|
169
|
+
tentative_distance = current.tentative_distance
|
170
|
+
raise "tentative distance can't be nil" if tentative_distance == nil
|
171
|
+
distance_between = map.distance_between(current, neighbor)
|
172
|
+
raise "distance between can't be nil" if distance_between == nil
|
173
|
+
net_distance = tentative_distance + distance_between
|
174
|
+
|
175
|
+
if neighbor.relable_node_as(net_distance) == :distance_was_udated
|
176
|
+
# p "set path"
|
177
|
+
pathTo[neighbor] = @current
|
178
|
+
# p "path #{@pathTo}"
|
179
|
+
end
|
180
|
+
}
|
181
|
+
end
|
182
|
+
unvisited.delete(@current)
|
183
|
+
|
184
|
+
# Are we done?
|
185
|
+
if unvisited.size == 0
|
186
|
+
save_path(@path)
|
187
|
+
else
|
188
|
+
# The next current node is the one with the least distance in the
|
189
|
+
# unvisited set
|
190
|
+
selection = map.nearest_unvisited_node_to_target
|
191
|
+
|
192
|
+
|
193
|
+
# Recur
|
194
|
+
CalculateShortestPath.new(selection, destination, map, path, @unvisited,
|
195
|
+
pathTo, tentative_distance_values)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
do_inits do |path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash|
|
200
|
+
|
201
|
+
# The conditional switches between the first and subsequent instances of the
|
202
|
+
# recursion (the algorithm is recursive in graph contexts)
|
203
|
+
if path_vector.nil?
|
204
|
+
|
205
|
+
def do_inits(path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash)
|
206
|
+
# The conditional switches between the first and subsequent instances of the
|
207
|
+
# recursion (the algorithm is recursive in graph contexts)
|
208
|
+
if path_vector.nil?
|
209
|
+
# Since path_vector isn't set up, this is the first iteration of the recursion
|
210
|
+
@tentative_distance_values = Hash.new
|
211
|
+
|
212
|
+
# This is the fundamental data structure for Dijkstra's algorithm, called
|
213
|
+
# "Q" in the Wikipedia description. It is a boolean hash that maps a
|
214
|
+
# node onto false or true according to whether it has been visited
|
215
|
+
|
216
|
+
@unvisited = Hash.new
|
217
|
+
|
218
|
+
# These initializations are directly from the description of the algorithm
|
219
|
+
map.nodes.each { |node| @unvisited[node] = true }
|
220
|
+
@unvisited.delete(map.origin)
|
221
|
+
map.nodes.each { |node| bind :node=>:distance_labeled_graph_node; node.set_tentative_distance_to(infinity) }
|
222
|
+
tentative_distance_values[map.origin] = 0
|
223
|
+
|
224
|
+
# The path array is kept in the outermost context and serves to store the
|
225
|
+
# return path. Each recurring context may add something to the array along
|
226
|
+
# the way. However, because of the nature of the algorithm, individual
|
227
|
+
# Context instances don't deliver "partial paths" as partial answers.
|
228
|
+
@path = Array.new
|
229
|
+
|
230
|
+
# The pathTo map is a local associative array that remembers the
|
231
|
+
# arrows between nodes through the array and erases them if we
|
232
|
+
# re-label a node with a shorter distance
|
233
|
+
|
234
|
+
@pathTo = Hash.new
|
235
|
+
|
236
|
+
else
|
237
|
+
|
238
|
+
# We are recurring. Just copy the values copied in from the previous iteration
|
239
|
+
# of the recursion
|
240
|
+
|
241
|
+
@tentative_distance_values = tentative_distance_values_hash
|
242
|
+
@unvisited = unvisited_hash
|
243
|
+
@path = path_vector
|
244
|
+
@pathTo = pathto_hash
|
245
|
+
end
|
246
|
+
end
|
247
|
+
# Since path_vector isn't set up, this is the first iteration of the recursion
|
248
|
+
|
249
|
+
@tentative_distance_values = Hash.new
|
250
|
+
|
251
|
+
# This is the fundamental data structure for Dijkstra's algorithm, called
|
252
|
+
# "Q" in the Wikipedia description. It is a boolean hash that maps a
|
253
|
+
# node onto false or true according to whether it has been visited
|
254
|
+
|
255
|
+
@unvisited = Hash.new
|
256
|
+
|
257
|
+
# These initializations are directly from the description of the algorithm
|
258
|
+
map.nodes.each { |node| @unvisited[node] = true }
|
259
|
+
@unvisited.delete(map.origin)
|
260
|
+
# p "map #{map.nodes}"
|
261
|
+
map.nodes.each { |node|
|
262
|
+
bind :node => :distance_labeled_graph_node;
|
263
|
+
node.set_tentative_distance_to(infinity)
|
264
|
+
# p "initialized node #{node.name}"
|
265
|
+
}
|
266
|
+
tentative_distance_values[map.origin] = 0
|
267
|
+
|
268
|
+
|
269
|
+
# The path array is kept in the outermost context and serves to store the
|
270
|
+
# return path. Each recurring context may add something to the array along
|
271
|
+
# the way. However, because of the nature of the algorithm, individual
|
272
|
+
# Context instances don't deliver "partial paths" as partial answers.
|
273
|
+
|
274
|
+
@path = Array.new
|
275
|
+
|
276
|
+
# The pathTo map is a local associative array that remembers the
|
277
|
+
# arrows between nodes through the array and erases them if we
|
278
|
+
# re-label a node with a shorter distance
|
279
|
+
|
280
|
+
@pathTo = Hash.new
|
281
|
+
|
282
|
+
else
|
283
|
+
|
284
|
+
# We are recurring. Just copy the values copied in from the previous iteration
|
285
|
+
# of the recursion
|
286
|
+
|
287
|
+
@tentative_distance_values = tentative_distance_values_hash
|
288
|
+
@unvisited = unvisited_hash
|
289
|
+
@path = path_vector
|
290
|
+
@pathTo = pathto_hash
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
class CalculateShortestPath
|
296
|
+
|
297
|
+
def pathTo
|
298
|
+
@pathTo
|
299
|
+
end
|
300
|
+
def east_neighbor; @east_neighbor end
|
301
|
+
def south_neighbor; @south_neighbor end
|
302
|
+
def path; @path end
|
303
|
+
|
304
|
+
def destination; @destination end
|
305
|
+
def tentative_distance_values; @tentative_distance_values end
|
306
|
+
|
307
|
+
# This is a shortcut to information that really belongs in the Map.
|
308
|
+
# To keep roles stateless, we hold the Map's unvisited structure in the
|
309
|
+
# Context object. We access it as though it were in the map
|
310
|
+
|
311
|
+
|
312
|
+
# Initialization
|
313
|
+
def rebind(origin_node, geometries)
|
314
|
+
@current = origin_node
|
315
|
+
@map = geometries
|
316
|
+
|
317
|
+
@east_neighbor = map.east_neighbor_of(origin_node)
|
318
|
+
@south_neighbor = map.south_neighbor_of(origin_node)
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
# public initialize. It's overloaded so that the public version doesn't
|
323
|
+
# have to pass a lot of crap; the initialize method takes care of
|
324
|
+
# setting up internal data structures on the first invocation. On
|
325
|
+
# recursion we override the defaults
|
326
|
+
|
327
|
+
def initialize(origin_node, target_node, geometries,
|
328
|
+
path_vector = nil, unvisited_hash = nil, pathto_hash = nil,
|
329
|
+
tentative_distance_values_hash = nil)
|
330
|
+
@destination = target_node
|
331
|
+
|
332
|
+
rebind(origin_node, geometries)
|
333
|
+
|
334
|
+
execute(path_vector, unvisited_hash, pathto_hash, tentative_distance_values_hash)
|
335
|
+
end
|
336
|
+
def each
|
337
|
+
path.each { |node| yield node }
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
# This method does a simple traversal of the data structures (following pathTo)
|
342
|
+
# to build the directed traversal vector for the minimum path
|
343
|
+
|
344
|
+
def save_path(pathVector)
|
345
|
+
node = destination
|
346
|
+
begin
|
347
|
+
pathVector << node
|
348
|
+
node = pathTo[node]
|
349
|
+
end while node != nil
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
@@ -0,0 +1,210 @@
|
|
1
|
+
def infinity; (2**(0.size * 8 -2) -1) end
|
2
|
+
# Data classes
|
3
|
+
Edge = Struct.new(:from, :to)
|
4
|
+
|
5
|
+
class Node
|
6
|
+
attr_reader :name
|
7
|
+
def initialize(n); @name = n end
|
8
|
+
def eql? (another_node)
|
9
|
+
# Nodes are == equal if they have the same name. This is explicitly
|
10
|
+
# defined here to call out the importance of the differnce between
|
11
|
+
# object equality and identity
|
12
|
+
name == another_node.name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
#
|
19
|
+
# --- Geometry is the interface to the data class that has all
|
20
|
+
# --- the information about the map. This is kind of silly in Ruby
|
21
|
+
#
|
22
|
+
class ManhattanGeometry
|
23
|
+
def initialize;
|
24
|
+
@nodes = Array.new
|
25
|
+
@distances = Hash.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def nodes; @nodes end
|
29
|
+
def distances
|
30
|
+
@distances
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
#
|
37
|
+
# --- Here are some test data
|
38
|
+
#
|
39
|
+
|
40
|
+
#noinspection RubyTooManyInstanceVariablesInspection
|
41
|
+
class Geometry_1 < ManhattanGeometry
|
42
|
+
def initialize
|
43
|
+
super()
|
44
|
+
|
45
|
+
names = %w('a' 'b' 'c' 'd' 'a' 'b' 'g' 'h' 'i')
|
46
|
+
|
47
|
+
3.times { |i|
|
48
|
+
3.times { |j|
|
49
|
+
nodes << Node.new(names[(i*3)+j])
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
# Aliases to help set up the grid. Grid is of Manhattan form:
|
56
|
+
#
|
57
|
+
# a - 2 - b - 3 - c
|
58
|
+
# | | |
|
59
|
+
# 1 2 1
|
60
|
+
# | | |
|
61
|
+
# d - 1 - e - 1 - f
|
62
|
+
# | |
|
63
|
+
# 2 4
|
64
|
+
# | |
|
65
|
+
# g - 1 - h - 2 - i
|
66
|
+
#
|
67
|
+
|
68
|
+
|
69
|
+
@node_a = nodes[0]
|
70
|
+
@node_b = nodes[1]
|
71
|
+
@node_c = nodes[2]
|
72
|
+
@node_d = nodes[3]
|
73
|
+
@node_e = nodes[4]
|
74
|
+
@node_f = nodes[5]
|
75
|
+
@node_g = nodes[6]
|
76
|
+
@node_h = nodes[7]
|
77
|
+
@node_i = nodes[8]
|
78
|
+
|
79
|
+
9.times { |i|
|
80
|
+
9.times { |j|
|
81
|
+
distances[Edge.new(nodes[i], nodes[j])] = infinity
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
distances[Edge.new(@node_a, @node_b)] = 2
|
86
|
+
distances[Edge.new(@node_b, @node_c)] = 3
|
87
|
+
distances[Edge.new(@node_c, @node_f)] = 1
|
88
|
+
distances[Edge.new(@node_f, @node_i)] = 4
|
89
|
+
distances[Edge.new(@node_b, @node_e)] = 2
|
90
|
+
distances[Edge.new(@node_e, @node_f)] = 1
|
91
|
+
distances[Edge.new(@node_a, @node_d)] = 1
|
92
|
+
distances[Edge.new(@node_d, @node_g)] = 2
|
93
|
+
distances[Edge.new(@node_g, @node_h)] = 1
|
94
|
+
distances[Edge.new(@node_h, @node_i)] = 2
|
95
|
+
distances[Edge.new(@node_d, @node_e)] = 1
|
96
|
+
distances.freeze
|
97
|
+
|
98
|
+
|
99
|
+
@next_down_the_street_from = Hash.new
|
100
|
+
@next_down_the_street_from[@node_a] = @node_b
|
101
|
+
@next_down_the_street_from[@node_b] = @node_c
|
102
|
+
@next_down_the_street_from[@node_d] = @node_e
|
103
|
+
@next_down_the_street_from[@node_e] = @node_f
|
104
|
+
@next_down_the_street_from[@node_g] = @node_h
|
105
|
+
@next_down_the_street_from[@node_h] = @node_i
|
106
|
+
@next_down_the_street_from.freeze
|
107
|
+
|
108
|
+
@next_along_the_avenue_from = Hash.new
|
109
|
+
@next_along_the_avenue_from[@node_a] = @node_d
|
110
|
+
@next_along_the_avenue_from[@node_b] = @node_e
|
111
|
+
@next_along_the_avenue_from[@node_c] = @node_f
|
112
|
+
@next_along_the_avenue_from[@node_d] = @node_g
|
113
|
+
@next_along_the_avenue_from[@node_f] = @node_i
|
114
|
+
@next_along_the_avenue_from.freeze
|
115
|
+
end
|
116
|
+
|
117
|
+
def east_neighbor_of(a); @next_down_the_street_from[a] end
|
118
|
+
def south_neighbor_of(a); @next_along_the_avenue_from[a] end
|
119
|
+
|
120
|
+
def root; @node_a end
|
121
|
+
def destination; @node_i end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
#noinspection RubyTooManyInstanceVariablesInspection
|
126
|
+
class ManhattanGeometry2 < ManhattanGeometry
|
127
|
+
def initialize
|
128
|
+
super()
|
129
|
+
names = %w('a' 'b' 'c' 'd' 'a' 'b' 'g' 'h' 'i' 'j' 'k')
|
130
|
+
|
131
|
+
11.times { |j| nodes << Node.new(names[j]) }
|
132
|
+
|
133
|
+
|
134
|
+
# Aliases to help set up the grid. Grid is of Manhattan form:
|
135
|
+
#
|
136
|
+
# a - 2 - b - 3 - c - 1 - j
|
137
|
+
# | | | |
|
138
|
+
# 1 2 1 |
|
139
|
+
# | | | |
|
140
|
+
# d - 1 - e - 1 - f 1
|
141
|
+
# | | |
|
142
|
+
# 2 4 |
|
143
|
+
# | | |
|
144
|
+
# g - 1 - h - 2 - i - 2 - k
|
145
|
+
|
146
|
+
|
147
|
+
#
|
148
|
+
@node_a = nodes[0]
|
149
|
+
@node_b = nodes[1]
|
150
|
+
@node_c = nodes[2]
|
151
|
+
@node_d = nodes[3]
|
152
|
+
@node_e = nodes[4]
|
153
|
+
@node_f = nodes[5]
|
154
|
+
@node_g = nodes[6]
|
155
|
+
@node_h = nodes[7]
|
156
|
+
@node_i = nodes[8]
|
157
|
+
@node_j = nodes[9]
|
158
|
+
@node_k = nodes[10]
|
159
|
+
|
160
|
+
11.times { |i|
|
161
|
+
11.times { |j|
|
162
|
+
distances[Edge.new(nodes[i], nodes[j])] = infinity
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
distances[Edge.new(@node_a, @node_b)] = 2
|
167
|
+
distances[Edge.new(@node_b, @node_c)] = 3
|
168
|
+
distances[Edge.new(@node_c, @node_f)] = 1
|
169
|
+
distances[Edge.new(@node_f, @node_i)] = 4
|
170
|
+
distances[Edge.new(@node_b, @node_e)] = 2
|
171
|
+
distances[Edge.new(@node_e, @node_f)] = 1
|
172
|
+
distances[Edge.new(@node_a, @node_d)] = 1
|
173
|
+
distances[Edge.new(@node_d, @node_g)] = 2
|
174
|
+
distances[Edge.new(@node_g, @node_h)] = 1
|
175
|
+
distances[Edge.new(@node_h, @node_i)] = 2
|
176
|
+
distances[Edge.new(@node_d, @node_e)] = 1
|
177
|
+
distances[Edge.new(@node_c, @node_j)] = 1
|
178
|
+
distances[Edge.new(@node_j, @node_k)] = 1
|
179
|
+
distances[Edge.new(@node_i, @node_k)] = 2
|
180
|
+
distances.freeze
|
181
|
+
|
182
|
+
|
183
|
+
@next_down_the_street_from = Hash.new
|
184
|
+
@next_down_the_street_from[@node_a] = @node_b
|
185
|
+
@next_down_the_street_from[@node_b] = @node_c
|
186
|
+
@next_down_the_street_from[@node_c] = @node_j
|
187
|
+
@next_down_the_street_from[@node_d] = @node_e
|
188
|
+
@next_down_the_street_from[@node_e] = @node_f
|
189
|
+
@next_down_the_street_from[@node_g] = @node_h
|
190
|
+
@next_down_the_street_from[@node_h] = @node_i
|
191
|
+
@next_down_the_street_from[@node_i] = @node_k
|
192
|
+
@next_down_the_street_from.freeze
|
193
|
+
|
194
|
+
@next_along_the_avenue_from = Hash.new
|
195
|
+
@next_along_the_avenue_from[@node_a] = @node_d
|
196
|
+
@next_along_the_avenue_from[@node_b] = @node_e
|
197
|
+
@next_along_the_avenue_from[@node_c] = @node_f
|
198
|
+
@next_along_the_avenue_from[@node_d] = @node_g
|
199
|
+
@next_along_the_avenue_from[@node_f] = @node_i
|
200
|
+
@next_along_the_avenue_from[@node_j] = @node_k
|
201
|
+
@next_along_the_avenue_from.freeze
|
202
|
+
end
|
203
|
+
|
204
|
+
def east_neighbor_of(a); @next_down_the_street_from[a] end
|
205
|
+
def south_neighbor_of(a); @next_along_the_avenue_from[a] end
|
206
|
+
|
207
|
+
def root; @node_a end
|
208
|
+
def destination; @node_k end
|
209
|
+
end
|
210
|
+
|