1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-03 07:24:58 +03:00
inform7/docs/WorldModelKit/S-mpr.html
2020-05-03 01:20:55 +01:00

440 lines
76 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>MapRouteFinding</title>
<link href="../docs-assets/Breadcrumbs.css" rel="stylesheet" rev="stylesheet" type="text/css">
<meta name="viewport" content="width=device-width initial-scale=1">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Content-Language" content="en-gb">
<link href="../docs-assets/Contents.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Progress.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Navigation.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Fonts.css" rel="stylesheet" rev="stylesheet" type="text/css">
<link href="../docs-assets/Base.css" rel="stylesheet" rev="stylesheet" type="text/css">
<script>
MathJax = {
tex: {
inlineMath: '$', '$'], ['\\(', '\\)'
},
svg: {
fontCache: 'global'
}
};
</script>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js">
</script>
<link href="../docs-assets/Colours.css" rel="stylesheet" rev="stylesheet" type="text/css">
</head>
<body class="commentary-font">
<nav role="navigation">
<h1><a href="../index.html">
<img src="../docs-src/Figures/Inform.png" height=72">
</a></h1>
<ul><li><a href="../compiler.html">compiler tools</a></li>
<li><a href="../other.html">other tools</a></li>
<li><a href="../extensions.html">extensions and kits</a></li>
<li><a href="../units.html">unit test tools</a></li>
</ul><h2>Extensions</h2><ul>
<li><a href="../basic_inform/index.html">basic_inform</a></li>
<li><a href="../standard_rules/index.html">standard_rules</a></li>
</ul><h2>Kits</h2><ul>
<li><a href="../BasicInformKit/index.html">BasicInformKit</a></li>
<li><a href="../BasicInformExtrasKit/index.html">BasicInformExtrasKit</a></li>
<li><a href="../CommandParserKit/index.html">CommandParserKit</a></li>
<li><a href="../EnglishLanguageKit/index.html">EnglishLanguageKit</a></li>
<li><a href="index.html"><span class="selectedlink">WorldModelKit</span></a></li>
</ul>
</nav>
<main role="main">
<!--Weave of 'MapRouteFinding' generated by Inweb-->
<div class="breadcrumbs">
<ul class="crumbs"><li><a href="../index.html">Home</a></li><li><a href="../extensions.html">Kits</a></li><li><a href="index.html">WorldModelKit</a></li><li><b>MapRouteFinding</b></li></ul></div>
<p class="purpose">Testing and changing the fundamental spatial relations.</p>
<ul class="toc"><li><a href="S-mpr.html#SP1">&#167;1. Map Route-Finding</a></li><li><a href="S-mpr.html#SP2">&#167;2. Cache Control</a></li><li><a href="S-mpr.html#SP3">&#167;3. Fast Route-Finding</a></li><li><a href="S-mpr.html#SP4">&#167;4. Slow Route-Finding</a></li></ul><hr class="tocbar">
<p class="commentary firstcommentary"><a id="SP1"></a><b>&#167;1. Map Route-Finding. </b>The general problem we have to solve here is: given \(x, y\in R\), where \(R\)
is the set of rooms and we write \(x\sim 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\sim r_2\sim ...\sim r_m = y\in 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="commentary">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="commentary">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="commentary">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 &gt; 50\) or so, the user is already likely to
be fighting for the last few bytes in readable memory.
</p>
<p class="commentary">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 &mdash; which might happen
due to a disagreement between extensions &mdash; "fast" wins.
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="plain-syntax">#</span><span class="identifier-syntax">ifndef</span><span class="plain-syntax"> </span><span class="identifier-syntax">FAST_ROUTE_FINDING</span><span class="plain-syntax">;</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">ifndef</span><span class="plain-syntax"> </span><span class="identifier-syntax">SLOW_ROUTE_FINDING</span><span class="plain-syntax">;</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">ifdef</span><span class="plain-syntax"> </span><span class="identifier-syntax">TARGET_GLULX</span><span class="plain-syntax">;</span>
<span class="reserved-syntax">Constant</span><span class="plain-syntax"> </span><span class="identifier-syntax">FAST_ROUTE_FINDING</span><span class="plain-syntax">;</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">ifnot</span><span class="plain-syntax">;</span>
<span class="reserved-syntax">Constant</span><span class="plain-syntax"> </span><span class="identifier-syntax">SLOW_ROUTE_FINDING</span><span class="plain-syntax">;</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">endif</span><span class="plain-syntax">;</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">endif</span><span class="plain-syntax">;</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">endif</span><span class="plain-syntax">;</span>
</pre>
<p class="commentary firstcommentary"><a id="SP2"></a><b>&#167;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>
<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="commentary">We keep track of (c) by watching for calls to <span class="extract"><span class="extract-syntax">SignalMapChange()</span></span> 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 <span class="extract"><span class="extract-syntax">room_index</span></span> and by the flags in the
<span class="extract"><span class="extract-syntax">DoorRoutingViable</span></span> array respectively.
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="reserved-syntax">Constant</span><span class="plain-syntax"> </span><span class="identifier-syntax">NUM_DOORS</span><span class="plain-syntax"> = </span><span class="identifier-syntax">ICOUNT_DOOR</span><span class="plain-syntax">;</span>
<span class="reserved-syntax">Constant</span><span class="plain-syntax"> </span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> = </span><span class="identifier-syntax">ICOUNT_ROOM</span><span class="plain-syntax">;</span>
<span class="reserved-syntax">Array</span><span class="plain-syntax"> </span><span class="identifier-syntax">DoorRoutingViable</span><span class="plain-syntax"> -&gt; </span><span class="identifier-syntax">NUM_DOORS</span><span class="plain-syntax">+1;</span>
<span class="identifier-syntax">Global</span><span class="plain-syntax"> </span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="identifier-syntax">Global</span><span class="plain-syntax"> </span><span class="identifier-syntax">last_filter</span><span class="plain-syntax">; </span><span class="identifier-syntax">Global</span><span class="plain-syntax"> </span><span class="identifier-syntax">last_use_doors</span><span class="plain-syntax">;</span>
<span class="plain-syntax">[ </span><span class="identifier-syntax">SignalMapChange</span><span class="plain-syntax">; </span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">; ];</span>
<span class="plain-syntax">[ </span><span class="identifier-syntax">MapRouteTo</span><span class="plain-syntax"> </span><span class="identifier-syntax">from</span><span class="plain-syntax"> </span><span class="reserved-syntax">to</span><span class="plain-syntax"> </span><span class="identifier-syntax">filter</span><span class="plain-syntax"> </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> </span><span class="identifier-syntax">count</span><span class="plain-syntax"> </span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="identifier-syntax">oyi</span><span class="plain-syntax"> </span><span class="identifier-syntax">ds</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="reserved-syntax">to</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">to</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">filter</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">filter</span><span class="plain-syntax">(</span><span class="identifier-syntax">from</span><span class="plain-syntax">) == </span><span class="constant-syntax">0</span><span class="plain-syntax">)) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">filter</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">filter</span><span class="plain-syntax">(</span><span class="reserved-syntax">to</span><span class="plain-syntax">) == </span><span class="constant-syntax">0</span><span class="plain-syntax">)) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">last_filter</span><span class="plain-syntax"> ~= </span><span class="identifier-syntax">filter</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">last_use_doors</span><span class="plain-syntax"> ~= </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">)) </span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">oyi</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">filter</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">filter</span><span class="plain-syntax">(</span><span class="identifier-syntax">oy</span><span class="plain-syntax">))) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> == -1) </span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> = </span><span class="identifier-syntax">oyi</span><span class="plain-syntax">++;</span>
<span class="plain-syntax"> } </span><span class="reserved-syntax">else</span><span class="plain-syntax"> {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">) </span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> = -1;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">oyi</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">ofclass</span><span class="plain-syntax"> </span><span class="identifier-syntax">K4_door</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">ds</span><span class="plain-syntax"> = </span><span class="reserved-syntax">false</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> &amp; </span><span class="constant-syntax">2</span><span class="plain-syntax">) ||</span>
<span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">open</span><span class="plain-syntax">) || ((</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">openable</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">hasnt</span><span class="plain-syntax"> </span><span class="identifier-syntax">locked</span><span class="plain-syntax">))) </span><span class="identifier-syntax">ds</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">DoorRoutingViable</span><span class="plain-syntax">-&gt;</span><span class="identifier-syntax">oyi</span><span class="plain-syntax"> ~= </span><span class="identifier-syntax">ds</span><span class="plain-syntax">) </span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">DoorRoutingViable</span><span class="plain-syntax">-&gt;</span><span class="identifier-syntax">oyi</span><span class="plain-syntax"> = </span><span class="identifier-syntax">ds</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">oyi</span><span class="plain-syntax">++;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">ifdef</span><span class="plain-syntax"> </span><span class="identifier-syntax">FAST_ROUTE_FINDING</span><span class="plain-syntax">; </span><span class="identifier-syntax">ComputeFWMatrix</span><span class="plain-syntax">(</span><span class="identifier-syntax">filter</span><span class="plain-syntax">, </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">); #</span><span class="identifier-syntax">endif</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">map_has_changed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">false</span><span class="plain-syntax">; </span><span class="identifier-syntax">last_filter</span><span class="plain-syntax"> = </span><span class="identifier-syntax">filter</span><span class="plain-syntax">; </span><span class="identifier-syntax">last_use_doors</span><span class="plain-syntax"> = </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">ifdef</span><span class="plain-syntax"> </span><span class="identifier-syntax">FAST_ROUTE_FINDING</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">count</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">FastCountRouteTo</span><span class="plain-syntax">(</span><span class="identifier-syntax">from</span><span class="plain-syntax">, </span><span class="reserved-syntax">to</span><span class="plain-syntax">, </span><span class="identifier-syntax">filter</span><span class="plain-syntax">, </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">FastRouteTo</span><span class="plain-syntax">(</span><span class="identifier-syntax">from</span><span class="plain-syntax">, </span><span class="reserved-syntax">to</span><span class="plain-syntax">, </span><span class="identifier-syntax">filter</span><span class="plain-syntax">, </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">ifnot</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">count</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">SlowCountRouteTo</span><span class="plain-syntax">(</span><span class="identifier-syntax">from</span><span class="plain-syntax">, </span><span class="reserved-syntax">to</span><span class="plain-syntax">, </span><span class="identifier-syntax">filter</span><span class="plain-syntax">, </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">SlowRouteTo</span><span class="plain-syntax">(</span><span class="identifier-syntax">from</span><span class="plain-syntax">, </span><span class="reserved-syntax">to</span><span class="plain-syntax">, </span><span class="identifier-syntax">filter</span><span class="plain-syntax">, </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> #</span><span class="identifier-syntax">endif</span><span class="plain-syntax">;</span>
<span class="plain-syntax">];</span>
</pre>
<p class="commentary firstcommentary"><a id="SP3"></a><b>&#167;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="commentary">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\leq a_{ij}\leq
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\leq a_{ij}\leq 357,913,941\).
</p>
<p class="commentary">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="commentary">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} &lt; 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, {\it Algorithms} (1988),
chapter 32.
</p>
<p class="commentary">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="displayed-code all-displayed-code code-font">
<span class="plain-syntax">#</span><span class="identifier-syntax">ifdef</span><span class="plain-syntax"> </span><span class="identifier-syntax">FAST_ROUTE_FINDING</span><span class="plain-syntax">;</span>
<span class="reserved-syntax">Array</span><span class="plain-syntax"> </span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax"> --&gt; </span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax">;</span>
<span class="plain-syntax">[ </span><span class="identifier-syntax">FastRouteTo</span><span class="plain-syntax"> </span><span class="identifier-syntax">from</span><span class="plain-syntax"> </span><span class="reserved-syntax">to</span><span class="plain-syntax"> </span><span class="identifier-syntax">filter</span><span class="plain-syntax"> </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> </span><span class="identifier-syntax">diri</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax"> </span><span class="identifier-syntax">dir</span><span class="plain-syntax"> </span><span class="identifier-syntax">oy</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">to</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">from</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="reserved-syntax">to</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">))/</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">i</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">diri</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">from</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="reserved-syntax">to</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">))%</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">=0; </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">dir</span><span class="plain-syntax"> </span><span class="reserved-syntax">ofclass</span><span class="plain-syntax"> </span><span class="identifier-syntax">K3_direction</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">i</span><span class="plain-syntax"> == </span><span class="identifier-syntax">diri</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">dir</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">++;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax">];</span>
<span class="plain-syntax">[ </span><span class="identifier-syntax">FastCountRouteTo</span><span class="plain-syntax"> </span><span class="identifier-syntax">from</span><span class="plain-syntax"> </span><span class="reserved-syntax">to</span><span class="plain-syntax"> </span><span class="identifier-syntax">filter</span><span class="plain-syntax"> </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> </span><span class="identifier-syntax">k</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">to</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">k</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">from</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="reserved-syntax">to</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">))/</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">k</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> -1;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">k</span><span class="plain-syntax">;</span>
<span class="plain-syntax">];</span>
<span class="plain-syntax">[ </span><span class="identifier-syntax">ComputeFWMatrix</span><span class="plain-syntax"> </span><span class="identifier-syntax">filter</span><span class="plain-syntax"> </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> </span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="identifier-syntax">ox</span><span class="plain-syntax"> </span><span class="identifier-syntax">oj</span><span class="plain-syntax"> </span><span class="identifier-syntax">axy</span><span class="plain-syntax"> </span><span class="identifier-syntax">ayj</span><span class="plain-syntax"> </span><span class="identifier-syntax">axj</span><span class="plain-syntax"> </span><span class="identifier-syntax">dir</span><span class="plain-syntax"> </span><span class="identifier-syntax">diri</span><span class="plain-syntax"> </span><span class="identifier-syntax">nd</span><span class="plain-syntax"> </span><span class="identifier-syntax">row</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">ox</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">) = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">row</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">IK1_Count</span><span class="plain-syntax">)*</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">for</span><span class="plain-syntax"> (</span><span class="identifier-syntax">diri</span><span class="plain-syntax">=0: </span><span class="identifier-syntax">diri</span><span class="plain-syntax">&lt;</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">: </span><span class="identifier-syntax">diri</span><span class="plain-syntax">++) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">ox</span><span class="plain-syntax"> = </span><span class="identifier-syntax">Map_Storage</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">row</span><span class="plain-syntax">+</span><span class="identifier-syntax">diri</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">ox</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">ox</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">)) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">) = </span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax"> + </span><span class="identifier-syntax">diri</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">continue</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> &amp;&amp; (</span><span class="identifier-syntax">ox</span><span class="plain-syntax"> </span><span class="reserved-syntax">ofclass</span><span class="plain-syntax"> </span><span class="identifier-syntax">K4_door</span><span class="plain-syntax">) &amp;&amp;</span>
<span class="plain-syntax"> ((</span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> &amp; </span><span class="constant-syntax">2</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">DoorRoutingViable</span><span class="plain-syntax">-&gt;(</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">IK4_Count</span><span class="plain-syntax">)))) {</span>
<span class="plain-syntax"> @</span><span class="identifier-syntax">push</span><span class="plain-syntax"> </span><span class="identifier-syntax">location</span><span class="plain-syntax">; </span><span class="identifier-syntax">location</span><span class="plain-syntax"> = </span><span class="identifier-syntax">oy</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">ox</span><span class="plain-syntax"> = </span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">door_to</span><span class="plain-syntax">();</span>
<span class="plain-syntax"> @</span><span class="identifier-syntax">pull</span><span class="plain-syntax"> </span><span class="identifier-syntax">location</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">ox</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">ox</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">)) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">) = </span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax"> + </span><span class="identifier-syntax">diri</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">continue</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">ox</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">axy</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">))/</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">axy</span><span class="plain-syntax"> &gt; </span><span class="constant-syntax">0</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oj</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">oj</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax"> &gt;= </span><span class="constant-syntax">0</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">ayj</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">oj</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">))/</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">ayj</span><span class="plain-syntax"> &gt; </span><span class="constant-syntax">0</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint "Is it faster to go from ", (name) ox, " to ",</span>
<span class="plain-syntax"> </span><span class="comment-syntax"> (name) oj, " via ", (name) oy, "?^";</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">axj</span><span class="plain-syntax"> = (</span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">oj</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">))/</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">axj</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">axy</span><span class="plain-syntax"> + </span><span class="identifier-syntax">ayj</span><span class="plain-syntax"> &lt; </span><span class="identifier-syntax">axj</span><span class="plain-syntax">)) {</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint "Yes^";</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">oj</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">) =</span>
<span class="plain-syntax"> (</span><span class="identifier-syntax">axy</span><span class="plain-syntax"> + </span><span class="identifier-syntax">ayj</span><span class="plain-syntax">)*</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax"> +</span>
<span class="plain-syntax"> (</span><span class="identifier-syntax">FWMatrix</span><span class="plain-syntax">--&gt;(</span><span class="identifier-syntax">ox</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">*</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax"> + </span><span class="identifier-syntax">oy</span><span class="plain-syntax">.</span><span class="identifier-syntax">room_index</span><span class="plain-syntax">))%</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax">];</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">ENDIF</span><span class="plain-syntax">;</span>
</pre>
<p class="commentary firstcommentary"><a id="SP4"></a><b>&#167;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 &mdash;
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 &mdash; in which
case we stop immediately &mdash; or the (directed) component containing \(R_y\)
has been exhausted &mdash; 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\leq 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="commentary">For each room \(R_z\), the property <span class="extract"><span class="extract-syntax">vector</span></span> 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 <span class="extract"><span class="extract-syntax">vector</span></span> direction
from where we currently stand until we reach \(R_y\). Since every room needs
a <span class="extract"><span class="extract-syntax">vector</span></span> value, this requires \(n\) words of storage. (The <span class="extract"><span class="extract-syntax">vector</span></span> 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="commentary">The method can be summed up thus:
</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 ({\it 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="commentary">To prove the correctness of this, we show inductively that after round \(n\)
we have set the <span class="extract"><span class="extract-syntax">vector</span></span> for every room having a shortest path to \(R_y\) of
length \(n\), and that every <span class="extract"><span class="extract-syntax">vector</span></span> points to a room having a <span class="extract"><span class="extract-syntax">vector</span></span> in
the direction of the shortest path from there to \(R_y\).
</p>
<pre class="displayed-code all-displayed-code code-font">
<span class="plain-syntax">#</span><span class="identifier-syntax">ifndef</span><span class="plain-syntax"> </span><span class="identifier-syntax">FAST_ROUTE_FINDING</span><span class="plain-syntax">;</span>
<span class="plain-syntax">[ </span><span class="identifier-syntax">SlowRouteTo</span><span class="plain-syntax"> </span><span class="identifier-syntax">from</span><span class="plain-syntax"> </span><span class="reserved-syntax">to</span><span class="plain-syntax"> </span><span class="identifier-syntax">filter</span><span class="plain-syntax"> </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> </span><span class="identifier-syntax">obj</span><span class="plain-syntax"> </span><span class="identifier-syntax">dir</span><span class="plain-syntax"> </span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> </span><span class="identifier-syntax">progressed</span><span class="plain-syntax"> </span><span class="identifier-syntax">sl</span><span class="plain-syntax"> </span><span class="identifier-syntax">through_door</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="reserved-syntax">to</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">to</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">obj</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">to</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> = </span><span class="constant-syntax">1</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint "Routing from ", (the) from, " to ", (the) to, "^";</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">while</span><span class="plain-syntax"> (</span><span class="reserved-syntax">true</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">progressed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">false</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint "Pass begins^";</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">obj</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">filter</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">filter</span><span class="plain-syntax">(</span><span class="identifier-syntax">obj</span><span class="plain-syntax">)))</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">dir</span><span class="plain-syntax"> </span><span class="reserved-syntax">ofclass</span><span class="plain-syntax"> </span><span class="identifier-syntax">K3_direction</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> = </span><span class="identifier-syntax">Map_Storage</span><span class="plain-syntax">--&gt;((</span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">IK1_Count</span><span class="plain-syntax">)*</span><span class="identifier-syntax">No_Directions</span><span class="plain-syntax"> + </span><span class="identifier-syntax">dir</span><span class="plain-syntax">.</span><span class="identifier-syntax">IK3_Count</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">continue</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint (the) obj, " &gt; ", (the) dir, " &gt; ", (the) in_direction, "^";</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> &amp;&amp; (</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> &amp;&amp; (</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> &gt; </span><span class="constant-syntax">0</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> &amp;&amp; ((</span><span class="identifier-syntax">filter</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">filter</span><span class="plain-syntax">(</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax">)))) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> = </span><span class="identifier-syntax">dir</span><span class="plain-syntax"> | </span><span class="identifier-syntax">WORD_HIGHBIT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint "* ", (the) obj, " vector is ", (the) dir, "^";</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">progressed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">continue</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> &amp;&amp; (</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> </span><span class="reserved-syntax">ofclass</span><span class="plain-syntax"> </span><span class="identifier-syntax">K4_door</span><span class="plain-syntax">) &amp;&amp;</span>
<span class="plain-syntax"> ((</span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> &amp; </span><span class="constant-syntax">2</span><span class="plain-syntax">) ||</span>
<span class="plain-syntax"> (</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">open</span><span class="plain-syntax">) ||</span>
<span class="plain-syntax"> ((</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">openable</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">in_direction</span><span class="plain-syntax"> </span><span class="reserved-syntax">hasnt</span><span class="plain-syntax"> </span><span class="identifier-syntax">locked</span><span class="plain-syntax">)))) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">sl</span><span class="plain-syntax"> = </span><span class="identifier-syntax">location</span><span class="plain-syntax">; </span><span class="identifier-syntax">location</span><span class="plain-syntax"> = </span><span class="identifier-syntax">obj</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">through_door</span><span class="plain-syntax"> = </span><span class="identifier-syntax">in_direction</span><span class="plain-syntax">.</span><span class="identifier-syntax">door_to</span><span class="plain-syntax">();</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">location</span><span class="plain-syntax"> = </span><span class="identifier-syntax">sl</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint "Through door is ", (the) through_door, "^";</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">through_door</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> &amp;&amp; (</span><span class="identifier-syntax">through_door</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> &amp;&amp; (</span><span class="identifier-syntax">through_door</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> &gt; </span><span class="constant-syntax">0</span><span class="plain-syntax">)</span>
<span class="plain-syntax"> &amp;&amp; ((</span><span class="identifier-syntax">filter</span><span class="plain-syntax"> == </span><span class="constant-syntax">0</span><span class="plain-syntax">) || (</span><span class="identifier-syntax">filter</span><span class="plain-syntax">(</span><span class="identifier-syntax">through_door</span><span class="plain-syntax">)))) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> = </span><span class="identifier-syntax">dir</span><span class="plain-syntax"> | </span><span class="identifier-syntax">WORD_HIGHBIT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="comment-syntax">rint "* ", (the) obj, " vector is ", (the) dir, "^";</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">progressed</span><span class="plain-syntax"> = </span><span class="reserved-syntax">true</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">continue</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">objectloop</span><span class="plain-syntax"> (</span><span class="identifier-syntax">obj</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) </span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> = </span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax"> &amp;~ </span><span class="identifier-syntax">WORD_HIGHBIT</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">from</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">progressed</span><span class="plain-syntax"> == </span><span class="reserved-syntax">false</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">from</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax">];</span>
<span class="plain-syntax">[ </span><span class="identifier-syntax">SlowCountRouteTo</span><span class="plain-syntax"> </span><span class="identifier-syntax">from</span><span class="plain-syntax"> </span><span class="reserved-syntax">to</span><span class="plain-syntax"> </span><span class="identifier-syntax">filter</span><span class="plain-syntax"> </span><span class="identifier-syntax">use_doors</span><span class="plain-syntax"> </span><span class="identifier-syntax">obj</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> -1;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="reserved-syntax">to</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> -1;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> == </span><span class="reserved-syntax">to</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="constant-syntax">0</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">from</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax"> &amp;&amp; </span><span class="reserved-syntax">to</span><span class="plain-syntax"> </span><span class="reserved-syntax">has</span><span class="plain-syntax"> </span><span class="identifier-syntax">mark_as_room</span><span class="plain-syntax">) {</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">obj</span><span class="plain-syntax"> = </span><span class="identifier-syntax">MapRouteTo</span><span class="plain-syntax">(</span><span class="identifier-syntax">from</span><span class="plain-syntax">,</span><span class="reserved-syntax">to</span><span class="plain-syntax">,</span><span class="identifier-syntax">filter</span><span class="plain-syntax">,</span><span class="identifier-syntax">use_doors</span><span class="plain-syntax">);</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">if</span><span class="plain-syntax"> (</span><span class="identifier-syntax">obj</span><span class="plain-syntax"> == </span><span class="reserved-syntax">nothing</span><span class="plain-syntax">) </span><span class="reserved-syntax">return</span><span class="plain-syntax"> -1;</span>
<span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax"> = </span><span class="constant-syntax">0</span><span class="plain-syntax">; </span><span class="identifier-syntax">obj</span><span class="plain-syntax"> = </span><span class="identifier-syntax">from</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">while</span><span class="plain-syntax"> ((</span><span class="identifier-syntax">obj</span><span class="plain-syntax"> ~= </span><span class="reserved-syntax">to</span><span class="plain-syntax">) &amp;&amp; (</span><span class="identifier-syntax">i</span><span class="plain-syntax">&lt;</span><span class="identifier-syntax">NUM_ROOMS</span><span class="plain-syntax">)) { </span><span class="identifier-syntax">i</span><span class="plain-syntax">++; </span><span class="identifier-syntax">obj</span><span class="plain-syntax"> = </span><span class="identifier-syntax">MapConnection</span><span class="plain-syntax">(</span><span class="identifier-syntax">obj</span><span class="plain-syntax">,</span><span class="identifier-syntax">obj</span><span class="plain-syntax">.</span><span class="identifier-syntax">vector</span><span class="plain-syntax">); }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> </span><span class="identifier-syntax">i</span><span class="plain-syntax">;</span>
<span class="plain-syntax"> }</span>
<span class="plain-syntax"> </span><span class="reserved-syntax">return</span><span class="plain-syntax"> -1;</span>
<span class="plain-syntax">];</span>
<span class="plain-syntax">#</span><span class="identifier-syntax">ENDIF</span><span class="plain-syntax">;</span>
</pre>
<nav role="progress"><div class="progresscontainer">
<ul class="progressbar"><li class="progressprev"><a href="S-wrl.html">&#10094;</a></li><li class="progresssection"><a href="S-msc.html">msc</a></li><li class="progresssection"><a href="S-gll.html">gll</a></li><li class="progresssection"><a href="S-zmc.html">zmc</a></li><li class="progresssection"><a href="S-lgh.html">lgh</a></li><li class="progresssection"><a href="S-lst.html">lst</a></li><li class="progresssection"><a href="S-ord.html">ord</a></li><li class="progresssection"><a href="S-act.html">act</a></li><li class="progresssection"><a href="S-act2.html">act2</a></li><li class="progresssection"><a href="S-fgr.html">fgr</a></li><li class="progresssection"><a href="S-otf.html">otf</a></li><li class="progresssection"><a href="S-prn.html">prn</a></li><li class="progresssection"><a href="S-wrl.html">wrl</a></li><li class="progresscurrent">mpr</li><li class="progresssection"><a href="S-rtp.html">rtp</a></li><li class="progresssection"><a href="S-tm.html">tm</a></li><li class="progresssection"><a href="S-tst.html">tst</a></li><li class="progresssection"><a href="S-chr.html">chr</a></li><li class="progresssection"><a href="S-str.html">str</a></li><li class="progressnext"><a href="S-rtp.html">&#10095;</a></li></ul></div>
</nav><!--End of weave-->
</main>
</body>
</html>