Moby 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|