mirror of
https://github.com/ganelson/inform.git
synced 2024-07-08 18:14:21 +03:00
414 lines
25 KiB
HTML
414 lines
25 KiB
HTML
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
|
<html>
|
|
<head>
|
|
<title>S/lt3</title>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
<meta http-equiv="Content-Language" content="en-gb">
|
|
<link href="inweb.css" rel="stylesheet" rev="stylesheet" type="text/css">
|
|
</head>
|
|
<body>
|
|
|
|
<!--Weave of 'S/mpr' generated by 7-->
|
|
<ul class="crumbs"><li><a href="../webs.html">★</a></li><li><a href="index.html">standard_rules Template Library</a></li><li><b>MapRouteFinding</b></li></ul><p class="purpose">Testing and changing the fundamental spatial relations.</p>
|
|
|
|
<ul class="toc"><li><a href="#SP1">§1. Map Route-Finding</a></li><li><a href="#SP2">§2. Cache Control</a></li><li><a href="#SP3">§3. Fast Route-Finding</a></li><li><a href="#SP4">§4. Slow Route-Finding</a></li></ul><hr class="tocbar">
|
|
|
|
<p class="inwebparagraph"><a id="SP1"></a><b>§1. Map Route-Finding. </b>The general problem we have to solve here is: given x, y∈ R, where R
|
|
is the set of rooms and we write x~ y if there is a map connection from
|
|
x to y,
|
|
</p>
|
|
|
|
<ul class="items"><li>(i) find the smallest m such that there exist x = r_1~ r_2~ ...~ r_m = y∈ R,
|
|
or determine that no such m exists, and
|
|
</li><li>(ii) find d, the first direction to take from x to lead to r_2, or
|
|
set d=0 if no such path exists or if m=1 so that x=y.
|
|
</li></ul>
|
|
<p class="inwebparagraph">Thus a typical outcome might be either "a shortest path from the Town Square
|
|
to the Hilltop takes 11 moves, starting by going northeast from the Town
|
|
Square", or alternatively "there's no path from the Town Square to the
|
|
Hilltop at all". Note that the length of the shortest path is unambiguous,
|
|
but that there might be many alternative paths of this minimum length:
|
|
we deliberately do not specify which path is chosen if so, and the two
|
|
algorithms used below do not necessarily choose the same one.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">Route-finding is not an easy operation in computation terms: the various
|
|
algorithms available have theoretical running times which are easy (if
|
|
sobering) to compute, but which are not in practice typical of what will
|
|
happen, because they are quite sensitive to the map in question. Are all
|
|
the rooms laid out in a long line? Are there clusters of connected rooms
|
|
like islands? Are there dense clumps of interconnecting rooms? Are there
|
|
huge but possibly time-saving loops? And so on. Overhead is also
|
|
important. We present a choice of two algorithms: the "fast" one
|
|
has a theoretical running time of O(n^3), where n is the number
|
|
of rooms, whereas the "slow" one runs in O(n^2), yet in practice
|
|
the fast one easily outperforms the slow on typical heavy-use cases with
|
|
large maps.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">The other issue is memory usage: we essentially have to strike a bargain
|
|
between speed and memory overhead. Our "slow" algorithm needs only
|
|
O(n) storage, whereas our "fast" algorithm needs O(n^2), and this
|
|
is very significant in the Z-machine where array space is in desperately
|
|
short supply and where, if n > 50 or so, the user is already likely to
|
|
be fighting for the last few bytes in readable memory.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">The user is therefore offered the choice, by selecting the use options
|
|
"Use fast route-finding" and "Use slow route-finding": and the defaults,
|
|
if neither option is explicitly set, are fast on Glulx and slow on the
|
|
Z-machine. If both use options are explicitly set — which might happen
|
|
due to a disagreement between extensions — "fast" wins.
|
|
</p>
|
|
|
|
|
|
<pre class="display">
|
|
<span class="plain">#ifndef FAST_ROUTE_FINDING;</span>
|
|
<span class="plain">#ifndef SLOW_ROUTE_FINDING;</span>
|
|
<span class="plain">#ifdef TARGET_GLULX;</span>
|
|
<span class="plain">Constant FAST_ROUTE_FINDING;</span>
|
|
<span class="plain">#ifnot;</span>
|
|
<span class="plain">Constant SLOW_ROUTE_FINDING;</span>
|
|
<span class="plain">#endif;</span>
|
|
<span class="plain">#endif;</span>
|
|
<span class="plain">#endif;</span>
|
|
</pre>
|
|
|
|
<p class="inwebparagraph"></p>
|
|
|
|
<p class="inwebparagraph"><a id="SP2"></a><b>§2. Cache Control. </b>We provide code to enable our route-finding algorithms to cache their partial
|
|
results from one usage to the next (though at present only the "fast"
|
|
algorithm does this). The difficulty here is that the result of a route
|
|
search depends on three things, any of which may change:
|
|
</p>
|
|
|
|
<p class="inwebparagraph"></p>
|
|
|
|
<ul class="items"><li>(a) which subset of rooms we are route-finding through;
|
|
</li><li>(b) which subset of doors we are allowing ourselves to use; and
|
|
</li><li>(c) the current map connections between rooms.
|
|
</li></ul>
|
|
<p class="inwebparagraph">We keep track of (c) by watching for calls to <code class="display"><span class="extract">SignalMapChange()</span></code> from the
|
|
routines in "WorldModel.i6t" which alter the map. (a) and (b), however,
|
|
require tracking from call to call what the current subset of rooms and
|
|
doors is. (It is not sufficient to remember the criteria used last time
|
|
and this time, because circumstances could have changed such that the
|
|
criteria produce a different outcome. For instance, searching through
|
|
lighted rooms and using unlocked doors will produce a different result
|
|
if a door has been locked or unlocked since last time, or if a room has
|
|
become lighted or not.) We store the set of applicable rooms and doors
|
|
by enumerating them in the property <code class="display"><span class="extract">room_index</span></code> and by the flags in the
|
|
<code class="display"><span class="extract">DoorRoutingViable</span></code> array respectively.
|
|
</p>
|
|
|
|
|
|
<pre class="display">
|
|
<span class="plain">Constant NUM_DOORS = ICOUNT_DOOR;</span>
|
|
<span class="plain">Constant NUM_ROOMS = ICOUNT_ROOM;</span>
|
|
|
|
<span class="plain">Array DoorRoutingViable -> NUM_DOORS+1;</span>
|
|
|
|
<span class="plain">Global map_has_changed = true;</span>
|
|
<span class="plain">Global last_filter; Global last_use_doors;</span>
|
|
|
|
<span class="plain">[ SignalMapChange; map_has_changed = true; ];</span>
|
|
|
|
<span class="plain">[ MapRouteTo from to filter use_doors count oy oyi ds;</span>
|
|
<span class="plain">if (from == nothing) return nothing;</span>
|
|
<span class="plain">if (to == nothing) return nothing;</span>
|
|
<span class="plain">if (from == to) return nothing;</span>
|
|
<span class="plain">if ((filter) && (filter(from) == 0)) return nothing;</span>
|
|
<span class="plain">if ((filter) && (filter(to) == 0)) return nothing;</span>
|
|
<span class="plain">if ((last_filter ~= filter) || (last_use_doors ~= use_doors)) map_has_changed = true;</span>
|
|
<span class="plain">oyi = 0;</span>
|
|
<span class="plain">objectloop (oy has mark_as_room) {</span>
|
|
<span class="plain">if ((filter == 0) || (filter(oy))) {</span>
|
|
<span class="plain">if (oy.room_index == -1) map_has_changed = true;</span>
|
|
<span class="plain">oy.room_index = oyi++;</span>
|
|
<span class="plain">} else {</span>
|
|
<span class="plain">if (oy.room_index >= 0) map_has_changed = true;</span>
|
|
<span class="plain">oy.room_index = -1;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">oyi = 0;</span>
|
|
<span class="plain">objectloop (oy ofclass K4_door) {</span>
|
|
<span class="plain">ds = false;</span>
|
|
<span class="plain">if ((use_doors & 2) ||</span>
|
|
<span class="plain">(oy has open) || ((oy has openable) && (oy hasnt locked))) ds = true;</span>
|
|
<span class="plain">if (DoorRoutingViable->oyi ~= ds) map_has_changed = true;</span>
|
|
<span class="plain">DoorRoutingViable->oyi = ds;</span>
|
|
<span class="plain">oyi++;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">if (map_has_changed) {</span>
|
|
<span class="plain">#ifdef FAST_ROUTE_FINDING; ComputeFWMatrix(filter, use_doors); #endif;</span>
|
|
<span class="plain">map_has_changed = false; last_filter = filter; last_use_doors = use_doors;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">#ifdef FAST_ROUTE_FINDING;</span>
|
|
<span class="plain">if (count) return FastCountRouteTo(from, to, filter, use_doors);</span>
|
|
<span class="plain">return FastRouteTo(from, to, filter, use_doors);</span>
|
|
<span class="plain">#ifnot;</span>
|
|
<span class="plain">if (count) return SlowCountRouteTo(from, to, filter, use_doors);</span>
|
|
<span class="plain">return SlowRouteTo(from, to, filter, use_doors);</span>
|
|
<span class="plain">#endif;</span>
|
|
<span class="plain">];</span>
|
|
</pre>
|
|
|
|
<p class="inwebparagraph"></p>
|
|
|
|
<p class="inwebparagraph"><a id="SP3"></a><b>§3. Fast Route-Finding. </b>The following is a form of Floyd's adaptation of Warshall's algorithm for
|
|
finding the transitive closure of a directed graph.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">We need to store a matrix which for each pair of rooms R_i and R_j
|
|
records a_{ij}, the shortest path length from R_i to R_j or 0 if no
|
|
path exists, and also d_{ij}, the first direction to take on leaving
|
|
R_i along a shortest path to R_j, or 0 if no path exists. For the sake
|
|
of economy we represent the directions as their instance counts (numbered
|
|
from 0 in order of creation), not as their direction object values, and
|
|
then store a single word for each pair (i, j): we store d_{ij} + D
|
|
a_{ij}. This restricts us on a signed 16-bit virtual machine, and with the
|
|
conventional set of D=12 directions, to the range 0<= a_{ij}<=
|
|
5461, that is, to path lengths of 5461 steps or fewer. A work of IF with
|
|
5461 rooms will not fit in the Z-machine anyway: such a work would be on
|
|
Glulx, which is 32-bit, and where 0<= a_{ij}<= 357,913,941.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">We begin with a_{ij} = 0 for all pairs except where there is a viable
|
|
map connection between R_i and R_j: for those we set a_{ij}=1 and
|
|
d_{ij} equal to the direction of that map connection.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">Following Floyd and Warshall we test if each known shortest path R_{x} to
|
|
R_{y} can be used to shorten the best known path from R_{x} to anywhere
|
|
else: that is, we look for cases where a_{xy} + a_{yj} < a_{xj}, since
|
|
those show that going from R_x to R_j via R_y takes fewer steps than
|
|
going directly. See for instance Robert Sedgewick, Algorithms (1988),
|
|
chapter 32.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">The trouble with the Floyd-Warshall algorithm is not so much that it takes
|
|
in principle O(n^3) time to construct the matrix: it does, but the
|
|
coefficient is low, and in the early stages of the outer loop the fact that
|
|
the vertex degree is at most D and usually much lower helps to reduce the
|
|
work further. The trouble is that there is no way to compute only the part
|
|
of the matrix we want: we have to have the entire thing, and that means
|
|
storing n^2 words of data, by which point we have computed not only the
|
|
fastest route from R_x to R_y but also the fastest route from anywhere
|
|
to anywhere else. Even when the original map is sparse, the Floyd-Warshall
|
|
matrix is not, and it is difficult to store in any very compressed way
|
|
without greatly increasing the complexity of the code. This is why we cache
|
|
the results: we might as well, since we had to build the entire memory
|
|
structure anyway, and it means the time expense is only paid once (or once
|
|
for every time the state of doors and map connections changes), and the
|
|
cache is useful for all future routes whatever their endpoints.
|
|
</p>
|
|
|
|
|
|
<pre class="display">
|
|
<span class="plain">#ifdef FAST_ROUTE_FINDING;</span>
|
|
<span class="plain">Array FWMatrix --> NUM_ROOMS*NUM_ROOMS;</span>
|
|
|
|
<span class="plain">[ FastRouteTo from to filter use_doors diri i dir oy;</span>
|
|
<span class="plain">if (from == to) return nothing;</span>
|
|
<span class="plain">i = (FWMatrix-->(from.room_index*NUM_ROOMS + to.room_index))/No_Directions;</span>
|
|
<span class="plain">if (i == 0) return nothing;</span>
|
|
<span class="plain">diri = (FWMatrix-->(from.room_index*NUM_ROOMS + to.room_index))%No_Directions;</span>
|
|
<span class="plain">i=0; objectloop (dir ofclass K3_direction) {</span>
|
|
<span class="plain">if (i == diri) return dir;</span>
|
|
<span class="plain">i++;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">return nothing;</span>
|
|
<span class="plain">];</span>
|
|
|
|
<span class="plain">[ FastCountRouteTo from to filter use_doors k;</span>
|
|
<span class="plain">if (from == to) return 0;</span>
|
|
<span class="plain">k = (FWMatrix-->(from.room_index*NUM_ROOMS + to.room_index))/No_Directions;</span>
|
|
<span class="plain">if (k == 0) return -1;</span>
|
|
<span class="plain">return k;</span>
|
|
<span class="plain">];</span>
|
|
|
|
<span class="plain">[ ComputeFWMatrix filter use_doors oy ox oj axy ayj axj dir diri nd row;</span>
|
|
<span class="plain">objectloop (oy has mark_as_room) if (oy.room_index >= 0)</span>
|
|
<span class="plain">objectloop (ox has mark_as_room) if (ox.room_index >= 0)</span>
|
|
<span class="plain">FWMatrix-->(oy.room_index*NUM_ROOMS + ox.room_index) = 0;</span>
|
|
|
|
<span class="plain">objectloop (oy has mark_as_room) if (oy.room_index >= 0) {</span>
|
|
<span class="plain">row = (oy.IK1_Count)*No_Directions;</span>
|
|
<span class="plain">for (diri=0: diri<No_Directions: diri++) {</span>
|
|
<span class="plain">ox = Map_Storage-->(row+diri);</span>
|
|
<span class="plain">if ((ox) && (ox has mark_as_room) && (ox.room_index >= 0)) {</span>
|
|
<span class="plain">FWMatrix-->(oy.room_index*NUM_ROOMS + ox.room_index) = No_Directions + diri;</span>
|
|
<span class="plain">continue;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">if (use_doors && (ox ofclass K4_door) &&</span>
|
|
<span class="plain">((use_doors & 2) || (DoorRoutingViable->(ox.IK4_Count)))) {</span>
|
|
<span class="plain">@push location; location = oy;</span>
|
|
<span class="plain">ox = ox.door_to();</span>
|
|
<span class="plain">@pull location;</span>
|
|
<span class="plain">if ((ox) && (ox has mark_as_room) && (ox.room_index >= 0)) {</span>
|
|
<span class="plain">FWMatrix-->(oy.room_index*NUM_ROOMS + ox.room_index) = No_Directions + diri;</span>
|
|
<span class="plain">continue;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
|
|
<span class="plain">objectloop (oy has mark_as_room) if (oy.room_index >= 0)</span>
|
|
<span class="plain">objectloop (ox has mark_as_room) if (ox.room_index >= 0) {</span>
|
|
<span class="plain">axy = (FWMatrix-->(ox.room_index*NUM_ROOMS + oy.room_index))/No_Directions;</span>
|
|
<span class="plain">if (axy > 0)</span>
|
|
<span class="plain">objectloop (oj has mark_as_room) if (oj.room_index >= 0) {</span>
|
|
<span class="plain">ayj = (FWMatrix-->(oy.room_index*NUM_ROOMS + oj.room_index))/No_Directions;</span>
|
|
<span class="plain">if (ayj > 0) {</span>
|
|
<span class="plain">!print "Is it faster to go from ", (name) ox, " to ",</span>
|
|
<span class="plain">! (name) oj, " via ", (name) oy, "?^";</span>
|
|
<span class="plain">axj = (FWMatrix-->(ox.room_index*NUM_ROOMS + oj.room_index))/</span>
|
|
<span class="plain">No_Directions;</span>
|
|
<span class="plain">if ((axj == 0) || (axy + ayj < axj)) {</span>
|
|
<span class="plain">!print "Yes^";</span>
|
|
<span class="plain">FWMatrix-->(ox.room_index*NUM_ROOMS + oj.room_index) =</span>
|
|
<span class="plain">(axy + ayj)*No_Directions +</span>
|
|
<span class="plain">(FWMatrix-->(ox.room_index*NUM_ROOMS + oy.room_index))%</span>
|
|
<span class="plain">No_Directions;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">];</span>
|
|
<span class="plain">#ENDIF;</span>
|
|
</pre>
|
|
|
|
<p class="inwebparagraph"></p>
|
|
|
|
<p class="inwebparagraph"><a id="SP4"></a><b>§4. Slow Route-Finding. </b>The alternative algorithm, used when only O(n) memory is available,
|
|
computes only some of the shortest paths leading to R_y, and is not cached —
|
|
both because the storage is likely to be reused often by other searches and
|
|
because there is little gain from doing so, given that a subsequent search
|
|
with different endpoints will not benefit from the results of this one. On
|
|
the other hand, to call it "slow" is a little unfair. It is somewhat like
|
|
Prim's algorithm for finding a minimum spanning tree, rooted at R_y, and
|
|
grows the tree outward from R_y until either R_x is reached — in which
|
|
case we stop immediately — or the (directed) component containing R_y
|
|
has been exhausted — in which case R_x, which must lie outside this, can
|
|
have no path to R_y. In principle, the running time is O(dn^2), where
|
|
d<= D is the maximum vertex degree and n is the number of rooms in
|
|
the component containing R_y: in practice the degree is often much less
|
|
than 12, while the algorithm finishes quickly in cases where R_y is
|
|
relatively isolated and inaccessible or where a shortish route does exist,
|
|
and those are very common cases in typical usage. There will be circumstances
|
|
where, because few routes need to be found and because of the shape of the
|
|
map, the "slow" algorithm will outperform the "fast" one: this is why
|
|
the user is allowed to control which algorithm is used.
|
|
</p>
|
|
|
|
<p class="inwebparagraph">For each room R_z, the property <code class="display"><span class="extract">vector</span></code> stores the direction object of
|
|
the way to go to its parent room in the tree rooted at R_y. Thus if the
|
|
algorithm succeeds in finding a route from R_x to R_y then we generate
|
|
the route by starting at R_x and repeatedly going in the <code class="display"><span class="extract">vector</span></code> direction
|
|
from where we currently stand until we reach R_y. Since every room needs
|
|
a <code class="display"><span class="extract">vector</span></code> value, this requires n words of storage. (The <code class="display"><span class="extract">vector</span></code> values
|
|
store only enough of the minimal spanning tree to go upwards through the
|
|
tree, but that's the only way we need to traverse it.)
|
|
</p>
|
|
|
|
<p class="inwebparagraph">The method can be summed up thus:
|
|
</p>
|
|
|
|
<p class="inwebparagraph"></p>
|
|
|
|
<ul class="items"><li>(a) Begin with every vector blank except that of R_y, the destination.
|
|
</li><li>(b) Repeatedly: For every room in the domain set, try each direction: if this
|
|
leads to a room whose vector was determined on the last round (not on
|
|
this one, as that may be a suboptimal route), set the vector to point to that
|
|
room.
|
|
</li><li>(c) Stop as soon as the vector from the origin is set, or when a round happens
|
|
in which no further vectors are found: in which case, we have completely
|
|
explored the component of the map from which the destination can be reached,
|
|
and the origin isn't in it, so we can return "no".
|
|
</li></ul>
|
|
<p class="inwebparagraph">To prove the correctness of this, we show inductively that after round n
|
|
we have set the <code class="display"><span class="extract">vector</span></code> for every room having a shortest path to R_y of
|
|
length n, and that every <code class="display"><span class="extract">vector</span></code> points to a room having a <code class="display"><span class="extract">vector</span></code> in
|
|
the direction of the shortest path from there to R_y.
|
|
</p>
|
|
|
|
|
|
<pre class="display">
|
|
<span class="plain">#ifndef FAST_ROUTE_FINDING;</span>
|
|
<span class="plain">[ SlowRouteTo from to filter use_doors obj dir in_direction progressed sl through_door;</span>
|
|
<span class="plain">if (from == nothing) return nothing;</span>
|
|
<span class="plain">if (to == nothing) return nothing;</span>
|
|
<span class="plain">if (from == to) return nothing;</span>
|
|
<span class="plain">objectloop (obj has mark_as_room) obj.vector = 0;</span>
|
|
<span class="plain">to.vector = 1;</span>
|
|
<span class="plain">!print "Routing from ", (the) from, " to ", (the) to, "^";</span>
|
|
<span class="plain">while (true) {</span>
|
|
<span class="plain">progressed = false;</span>
|
|
<span class="plain">!print "Pass begins^";</span>
|
|
<span class="plain">objectloop (obj has mark_as_room)</span>
|
|
<span class="plain">if ((filter == 0) || (filter(obj)))</span>
|
|
<span class="plain">if (obj.vector == 0)</span>
|
|
<span class="plain">objectloop (dir ofclass K3_direction) {</span>
|
|
<span class="plain">in_direction = Map_Storage-->((obj.IK1_Count)*No_Directions + dir.IK3_Count);</span>
|
|
<span class="plain">if (in_direction == nothing) continue;</span>
|
|
<span class="plain">!print (the) obj, " > ", (the) dir, " > ", (the) in_direction, "^";</span>
|
|
<span class="plain">if ((in_direction)</span>
|
|
<span class="plain">&& (in_direction has mark_as_room)</span>
|
|
<span class="plain">&& (in_direction.vector > 0)</span>
|
|
<span class="plain">&& ((filter == 0) || (filter(in_direction)))) {</span>
|
|
<span class="plain">obj.vector = dir | WORD_HIGHBIT;</span>
|
|
<span class="plain">!print "* ", (the) obj, " vector is ", (the) dir, "^";</span>
|
|
<span class="plain">progressed = true;</span>
|
|
<span class="plain">continue;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">if (use_doors && (in_direction ofclass K4_door) &&</span>
|
|
<span class="plain">((use_doors & 2) ||</span>
|
|
<span class="plain">(in_direction has open) ||</span>
|
|
<span class="plain">((in_direction has openable) && (in_direction hasnt locked)))) {</span>
|
|
<span class="plain">sl = location; location = obj;</span>
|
|
<span class="plain">through_door = in_direction.door_to();</span>
|
|
<span class="plain">location = sl;</span>
|
|
<span class="plain">!print "Through door is ", (the) through_door, "^";</span>
|
|
<span class="plain">if ((through_door)</span>
|
|
<span class="plain">&& (through_door has mark_as_room)</span>
|
|
<span class="plain">&& (through_door.vector > 0)</span>
|
|
<span class="plain">&& ((filter == 0) || (filter(through_door)))) {</span>
|
|
<span class="plain">obj.vector = dir | WORD_HIGHBIT;</span>
|
|
<span class="plain">!print "* ", (the) obj, " vector is ", (the) dir, "^";</span>
|
|
<span class="plain">progressed = true;</span>
|
|
<span class="plain">continue;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">objectloop (obj has mark_as_room) obj.vector = obj.vector &~ WORD_HIGHBIT;</span>
|
|
<span class="plain">if (from.vector) return from.vector;</span>
|
|
<span class="plain">if (progressed == false) return from.vector;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">];</span>
|
|
|
|
<span class="plain">[ SlowCountRouteTo from to filter use_doors obj i;</span>
|
|
<span class="plain">if (from == nothing) return -1;</span>
|
|
<span class="plain">if (to == nothing) return -1;</span>
|
|
<span class="plain">if (from == to) return 0;</span>
|
|
<span class="plain">if (from has mark_as_room && to has mark_as_room) {</span>
|
|
<span class="plain">obj = MapRouteTo(from,to,filter,use_doors);</span>
|
|
<span class="plain">if (obj == nothing) return -1;</span>
|
|
<span class="plain">i = 0; obj = from;</span>
|
|
<span class="plain">while ((obj ~= to) && (i<NUM_ROOMS)) { i++; obj = MapConnection(obj,obj.vector); }</span>
|
|
<span class="plain">return i;</span>
|
|
<span class="plain">}</span>
|
|
<span class="plain">return -1;</span>
|
|
<span class="plain">];</span>
|
|
<span class="plain">#ENDIF;</span>
|
|
|
|
</pre>
|
|
|
|
<p class="inwebparagraph"></p>
|
|
|
|
<hr class="tocbar">
|
|
<ul class="toc"><li><a href="S-lt3.html">Back to 'ListWriter Template'</a></li><li><a href="S-nt.html">Continue with 'Number Template'</a></li></ul><hr class="tocbar">
|
|
<!--End of weave-->
|
|
</body>
|
|
</html>
|
|
|