1519 lines
40 KiB
C
1519 lines
40 KiB
C
/*
|
||
* mzSearch.c --
|
||
*
|
||
* Search strategy for maze router.
|
||
* Low level of Maze router (finding next interesting point) is done in
|
||
* mzSearchRight.c etc.
|
||
*
|
||
* *********************************************************************
|
||
* * Copyright (C) 1988, 1990 Michael H. Arnold and the Regents of the *
|
||
* * University of California. *
|
||
* * Permission to use, copy, modify, and distribute this *
|
||
* * software and its documentation for any purpose and without *
|
||
* * fee is hereby granted, provided that the above copyright *
|
||
* * notice appear in all copies. The University of California *
|
||
* * makes no representations about the suitability of this *
|
||
* * software for any purpose. It is provided "as is" without *
|
||
* * express or implied warranty. Export of this software outside *
|
||
* * of the United States of America may require an export license. *
|
||
* *********************************************************************
|
||
*
|
||
* SEARCH STRATEGY:
|
||
*
|
||
* Partial paths are expanded one interesting point at a time - that is a
|
||
* path is expanded to the next interesting point left, right, up and down
|
||
* and vias to neighboring layers are considered, then a new (though possibly
|
||
* one of the extensions just created) path is selected for expansion.
|
||
*
|
||
* The "search strategy" employed determines which partial-path is
|
||
* chosen for exansion at each stage.
|
||
*
|
||
* A windowed search strategy is used. At any given time the window
|
||
* bounds define a minimum and maximum distance to the goal. Partial paths
|
||
* are divided into three groups: those farther from the goal than the
|
||
* window, those within the window, and those nearer to the goal than the
|
||
* window. The window begins at the start point and is slowly shifted
|
||
* toward the goal over time. Normally the partial-path of least
|
||
* estimated-total-cost WITHIN
|
||
* the window is chosen for expansion, although a further path (i.e. one
|
||
* beyond the window) that has exceptionally low estimated-total-cost is
|
||
* sometimes chosen instead. When the trailing edge of the window reaches
|
||
* the goal, the search is deemed complete and the lowest cost complete
|
||
* path found is returned. However if no complete-path has been found
|
||
* searching will continue until the first complete path is found.
|
||
* The idea behind the windowed search is to
|
||
* expand the paths that promise to give the best (lowest cost) solution, but
|
||
* to slowly shift attention toward the goal to avoid the VERY long time
|
||
* it would take to examine all possible paths.
|
||
*
|
||
* In the case of many partial paths within the window, the above search may
|
||
* be too scattered; "blooming" is used to give a local-focus to the search
|
||
* by examining a number of expansions
|
||
* from a given partial path before going on to consider other paths. Blooming
|
||
* is equivalent to searching the move tree in chess before applyng static
|
||
* evaluations of "final" positions. In practice it makes the router behave
|
||
* much better.
|
||
*
|
||
* The search strategy is implemented with three heaps and four stacks:
|
||
*
|
||
* mzMaxToGoHeap - keeps partial paths NEARER the goal than the window.
|
||
* these paths are not yet eligible for expansion.
|
||
* Whenever the window is moved, the top elements of
|
||
* this heap (i.e. those furthest from the goal) are
|
||
* moved onto the mzMinCostHeap (i.e. into the window).
|
||
*
|
||
* mzMinCostHeap - keeps partial paths inside the window. the top of this
|
||
* heap, i.e. the least cost path inside
|
||
* the window, is compared with the least (penalized) cost of
|
||
* the paths beyond the window, and the lesser of these
|
||
* two is chosen for expansion.
|
||
*
|
||
* mzMinAdjCostHeap - keeps partial paths that are further from the goal
|
||
* than the
|
||
* window. These paths are sorted by adjusted cost.
|
||
* Adjusted cost is estimated-total-cost plus a penalty
|
||
* proportial to the distance of the path from the window.
|
||
* The ordering of the paths is independent of the current
|
||
* window position - so a "reference position" is used to
|
||
* avoid recomputing adjusted costs for these paths
|
||
* everytime the window moves. The adjusted cost of the
|
||
* top (least cost) element is normalized to the current
|
||
* window position before comparison with the least cost
|
||
* path on the mzMinCostHeap. The idea behind the
|
||
* mzMinAdjCostHeap is
|
||
* to discourage searching of paths beyond the window, but
|
||
* to do so in a gentle and flexible way, so that
|
||
* behaviour will degrade gracefully under unusual
|
||
* circumstances rather than fail catastrophically.
|
||
* For example, if the window moves too fast and all the
|
||
* reasonable paths are left behind, the search will
|
||
* degenerate to a simple cost-based search biased towards
|
||
* paths nearer to completion - a startegy that is
|
||
* adequate in many situations.
|
||
*
|
||
* mzBloomStack - paths in current local focus = all expansions of a given
|
||
* partial-path that do not exceed a given estimated-cost
|
||
* (also see stacks below)
|
||
*
|
||
* mzStraightStack - focus paths being extended in straight line.
|
||
*
|
||
* mzDownHillStack - focus paths followed only until cost increases.
|
||
*
|
||
* mzWalkStack - paths inside walk (i.e. inside blocked area adjacent to
|
||
* destination.
|
||
*
|
||
* If the walk stack is not empty the next path is taken from there (thus
|
||
* paths that have reached the threshold of a dest area are given priority.)
|
||
*
|
||
* Next priority is given to the down hill stack, thus a focus path is always
|
||
* expanded until the estimated cost increases.
|
||
*
|
||
* Next the straight stack is processed. The purpose of this stack is to
|
||
* consider straight extensions in all directions to some given minimum
|
||
* distance.
|
||
*
|
||
* Next the bloom stack itself is processed. All extensions derived from
|
||
* the bloom stack are placed on one of the stacks until a fixed cost increment
|
||
* is exceeded (in practice a small increment is used, thus the local focus
|
||
* is only extended in straight lines, and then downhill).
|
||
*
|
||
* If all the stacks are empty, a new focus is pickd from the heaps and
|
||
* the bloom stack is initialized with it.
|
||
*
|
||
*/
|
||
|
||
#ifndef lint
|
||
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/mzrouter/mzSearch.c,v 1.2 2010/06/24 12:37:19 tim Exp $";
|
||
#endif /* not lint */
|
||
|
||
#include <stdio.h>
|
||
|
||
#include "utils/magic.h"
|
||
#include "utils/geometry.h"
|
||
#include "utils/geofast.h"
|
||
#include "utils/hash.h"
|
||
#include "utils/heap.h"
|
||
#include "tiles/tile.h"
|
||
#include "database/database.h"
|
||
#include "utils/signals.h"
|
||
#include "textio/textio.h"
|
||
#include "utils/malloc.h"
|
||
#include "utils/list.h"
|
||
#include "debug/debug.h"
|
||
#include "utils/styles.h"
|
||
#include "windows/windows.h"
|
||
#include "dbwind/dbwind.h"
|
||
#include "mzrouter/mzrouter.h"
|
||
#include "mzrouter/mzInternal.h"
|
||
|
||
/* Forward declarations */
|
||
|
||
extern void mzExtendViaLRContacts();
|
||
extern void mzExtendViaUDContacts();
|
||
extern void mzBloomInit();
|
||
extern void mzMakeStatReport();
|
||
extern void mzExtendPath(RoutePath *);
|
||
|
||
|
||
/*
|
||
* ----------------------------------------------------------------------------
|
||
*
|
||
* mzSearch --
|
||
*
|
||
* Find a path between one of the starting terminals and the
|
||
* destination. The path must lie within the
|
||
* area mzBoundingRect.
|
||
*
|
||
* Results:
|
||
* Returns a pointer to best complete RoutePath found (NULL
|
||
* if none found). This RoutePath may be passed to
|
||
* MZPaintPath() to be converted into paint.
|
||
*
|
||
* Side effects:
|
||
* Allocates RoutePaths from our own private storage area.
|
||
* The caller must copy these to permanent storage with
|
||
* mzCopyPath() prior to calling MZClean; otherwise,
|
||
* the returned RoutePath will be lost.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
RoutePath *
|
||
mzSearch(mzResult)
|
||
int *mzResult;
|
||
{
|
||
RoutePath *path; /* solution */
|
||
bool morePartialPaths = TRUE;
|
||
bool bloomLimitHit = FALSE;
|
||
bool windowSweepDoneAndCompletePathFound = FALSE;
|
||
|
||
/* Report intial cost estimate */
|
||
if(mzVerbosity>=VERB_STATS)
|
||
{
|
||
TxPrintf("Initial Cost Estimate: %.0f\n",
|
||
(double)(mzInitialEstimate));
|
||
}
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("\nBEGINNING SEARCH.\n");
|
||
TxPrintf("\tmzWRate = %.0f\n", (double)(mzWRate));
|
||
TxPrintf("\tmzWInitialMinToGo = %.0f\n", (double)(mzWInitialMinToGo));
|
||
TxPrintf("\tmzWInitialMaxToGo = %.0f\n", (double)(mzWInitialMaxToGo));
|
||
TxPrintf("\tmzBloomDeltaCost = %.0f\n", (double)(mzBloomDeltaCost));
|
||
}
|
||
|
||
while (morePartialPaths &&
|
||
!windowSweepDoneAndCompletePathFound &&
|
||
!bloomLimitHit &&
|
||
!SigInterruptPending)
|
||
/* Find next path to extend from */
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("\nABOUT TO SELECT NEXT PATH TO EXTEND.\n");
|
||
TxMore("");
|
||
}
|
||
|
||
/* pop a stack */
|
||
path = NULL;
|
||
if(mzWalkStack != NULL)
|
||
{
|
||
mzPathSource = SOURCE_WALK;
|
||
path = (RoutePath *) ListPop(&mzWalkStack);
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("POPPING TOP OF WALK STACK for extension.\n");
|
||
mzPrintPathHead(path);
|
||
}
|
||
}
|
||
else if(mzDownHillStack != NULL)
|
||
{
|
||
mzPathSource = SOURCE_DOWNHILL;
|
||
path = (RoutePath *) ListPop(&mzDownHillStack);
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("POPPING TOP OF DOWNHILL STACK for extension.\n");
|
||
mzPrintPathHead(path);
|
||
}
|
||
}
|
||
else if(mzStraightStack != NULL)
|
||
{
|
||
mzPathSource = SOURCE_STRAIGHT;
|
||
path = (RoutePath *) ListPop(&mzStraightStack);
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("POPPING TOP OF STRAIGHT STACK for extension.\n");
|
||
mzPrintPathHead(path);
|
||
}
|
||
}
|
||
else if(mzBloomStack != NULL)
|
||
{
|
||
mzPathSource = SOURCE_BLOOM;
|
||
path = (RoutePath *) ListPop(&mzBloomStack);
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("POPPING TOP OF BLOOM STACK for extension.\n");
|
||
mzPrintPathHead(path);
|
||
}
|
||
}
|
||
|
||
/* Stacks contained a path, go about the buisness of extending it */
|
||
if(path)
|
||
{
|
||
/* Check hashtable to see if path already obsolete,
|
||
* (i.e. cheaper path to its enpt found.)
|
||
*/
|
||
{
|
||
HashEntry *he;
|
||
PointKey pk;
|
||
RoutePath *hashedPath;
|
||
|
||
pk.pk_point = path->rp_entry;
|
||
pk.pk_rLayer = path->rp_rLayer;
|
||
pk.pk_orient = path->rp_orient;
|
||
#if SIZEOF_VOID_P == 8
|
||
pk.pk_buffer = 0; /* Otherwise the hash function screws up */
|
||
#endif
|
||
|
||
he = HashFind(&mzPointHash, (char *) &pk);
|
||
hashedPath = (RoutePath *) HashGetValue(he);
|
||
ASSERT(hashedPath!=NULL,"mzFindPath");
|
||
|
||
if (path!=hashedPath)
|
||
{
|
||
/* DEBUG - report path rejected due to hash value. */
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("HASH LOOKUP reveals better path, REJECT path.\n");
|
||
}
|
||
|
||
/* better path to this pt already exists,
|
||
* skip to next path
|
||
*/
|
||
continue;
|
||
}
|
||
}
|
||
|
||
/* Make sure blockage planes have been generated around the path
|
||
* end.
|
||
*/
|
||
{
|
||
Point *point = &(path->rp_entry);
|
||
Tile *tp = TiSrPointNoHint(mzHBoundsPlane, point);
|
||
|
||
if (TiGetType(tp)==TT_SPACE ||
|
||
point->p_x == LEFT(tp) || point->p_x == RIGHT(tp))
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("Path ends on vertical boundary of blockage");
|
||
TxPrintf(" planes, BLOCKAGE PLANES BEING EXTENDED.\n");
|
||
}
|
||
|
||
mzExtendBlockBounds(point);
|
||
if(SigInterruptPending) continue;
|
||
}
|
||
|
||
else
|
||
{
|
||
|
||
tp = TiSrPointNoHint(mzVBoundsPlane, point);
|
||
if (point->p_y == BOTTOM(tp) || point->p_y == TOP(tp))
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("Path ends on horizontal boundary");
|
||
TxPrintf("of blockage planes, BLOCKAGE PLANES");
|
||
TxPrintf("BEING EXTENDED.\n");
|
||
}
|
||
|
||
mzExtendBlockBounds(point);
|
||
if(SigInterruptPending) continue;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* DEBUG - if single-stepping, print data, show path end visually,
|
||
* and pause.
|
||
*/
|
||
if (DebugIsSet(mzDebugID, mzDebStep))
|
||
{
|
||
Rect box;
|
||
CellDef *boxDef;
|
||
|
||
/* print stats on this path */
|
||
{
|
||
int i;
|
||
RoutePath *p;
|
||
|
||
/* path # */
|
||
TxPrintf("READY TO EXTEND PATH ");
|
||
TxPrintf("(blooms: %d, points-processed: %d):\n",
|
||
mzNumBlooms,
|
||
mzNumPaths);
|
||
mzPrintPathHead(path);
|
||
|
||
/* # of segments in path segments */
|
||
i=0;
|
||
for(p=path; p->rp_back!=0; p=p->rp_back)
|
||
i++;
|
||
TxPrintf(" (%d segments in path)\n", i);
|
||
}
|
||
|
||
/* move box to path end-point */
|
||
if(ToolGetBox(&boxDef,&box))
|
||
{
|
||
int deltaX = box.r_xtop - box.r_xbot;
|
||
int deltaY = box.r_ytop - box.r_ybot;
|
||
box.r_ll = path->rp_entry;
|
||
box.r_ur.p_x = path->rp_entry.p_x + deltaX;
|
||
box.r_ur.p_y = path->rp_entry.p_y + deltaY;
|
||
DBWSetBox(mzRouteUse->cu_def, &box);
|
||
WindUpdate();
|
||
}
|
||
|
||
/* wait for user to continue */
|
||
TxMore("");
|
||
}
|
||
|
||
/* Extend Path */
|
||
mzExtendPath(path);
|
||
|
||
/* update statistics */
|
||
mzNumPaths++; /* increment number of paths processed */
|
||
if(--mzPathsTilReport == 0)
|
||
{
|
||
mzPathsTilReport = mzReportInterval;
|
||
mzMakeStatReport();
|
||
}
|
||
|
||
}
|
||
else
|
||
/* stacks are empty, choose path from heaps to initial them with */
|
||
{
|
||
HeapEntry maxToGoTopBuf, minCostTopBuf, buf;
|
||
HeapEntry *maxToGoTop, *minCostTop, *minAdjCostTop;
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("BLOOM STACK EMPTY. ");
|
||
TxPrintf("Choosing path from heaps to initial it with.\n");
|
||
}
|
||
|
||
/* If the bloom limit has been exceeded, stop searching */
|
||
if(mzBloomLimit >0 &&
|
||
mzNumBlooms > mzBloomLimit)
|
||
{
|
||
if(mzVerbosity>=VERB_BRIEF)
|
||
{
|
||
TxPrintf("Bloom limit (%d) hit.\n", mzBloomLimit);
|
||
}
|
||
|
||
bloomLimitHit = TRUE;
|
||
continue;
|
||
}
|
||
|
||
/* set window thresholds */
|
||
{
|
||
dlong offset;
|
||
|
||
offset = (dlong) (mzWRate * mzNumBlooms);
|
||
|
||
if(offset <= mzWInitialMinToGo)
|
||
{
|
||
mzWindowMinToGo = mzWInitialMinToGo - offset;
|
||
}
|
||
else
|
||
{
|
||
mzWindowMinToGo = 0;
|
||
}
|
||
|
||
if(offset <= mzWInitialMaxToGo)
|
||
{
|
||
mzWindowMaxToGo = mzWInitialMaxToGo - offset;
|
||
}
|
||
else
|
||
{
|
||
mzWindowMaxToGo = 0;
|
||
}
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("New window thresholds: ");
|
||
TxPrintf("windowMinToGo = %.0f, ",
|
||
(double)mzWindowMinToGo);
|
||
TxPrintf("windowMaxToGo = %.0f\n ",
|
||
(double)mzWindowMaxToGo);
|
||
}
|
||
}
|
||
|
||
/* If window sweep is complete, and a complete path
|
||
* has been found, stop searching.
|
||
*/
|
||
if((mzWindowMaxToGo == 0) &&
|
||
HeapLookAtTop(&mzMinCostCompleteHeap))
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("WINDOW SWEEP DONE AND COMPLETE PATH EXISTS.");
|
||
TxPrintf(" Stop searching.\n");
|
||
}
|
||
|
||
windowSweepDoneAndCompletePathFound = TRUE;
|
||
continue;
|
||
}
|
||
|
||
/* Move points that meet the minTogo threshold to window
|
||
* (Points are moved from the MaxToGo heap to the minCost heap)
|
||
*/
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("Moving paths into window ");
|
||
TxPrintf("(maxTogoHeap -> minCostHeap): \n");
|
||
}
|
||
|
||
while((maxToGoTop=HeapRemoveTop(&mzMaxToGoHeap,
|
||
&maxToGoTopBuf)) != NULL &&
|
||
(maxToGoTop->he_union.hu_dlong >= mzWindowMinToGo))
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
mzPrintPathHead((RoutePath*)(maxToGoTop->he_id));
|
||
}
|
||
|
||
HeapAddDLong(&mzMinCostHeap,
|
||
((RoutePath *)(maxToGoTop->he_id))->rp_cost,
|
||
(char *) (maxToGoTop->he_id));
|
||
}
|
||
if(maxToGoTop!=NULL)
|
||
{
|
||
HeapAddDLong(&mzMaxToGoHeap,
|
||
maxToGoTop->he_union.hu_dlong,
|
||
(char *) (maxToGoTop->he_id));
|
||
}
|
||
}
|
||
|
||
/* Clear top of minCostHeap of points that no longer meet the
|
||
* mzWindowMaxToGo threshold.
|
||
* (minCostHeap -> minAdjCostHeap)
|
||
*/
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("Moving paths out of window ");
|
||
TxPrintf("(minCostHeap -> minAdjCostHeap): \n");
|
||
}
|
||
|
||
while((minCostTop=HeapRemoveTop(&mzMinCostHeap,
|
||
&minCostTopBuf))!=NULL &&
|
||
(((RoutePath *)(minCostTop->he_id))->rp_togo >
|
||
mzWindowMaxToGo))
|
||
{
|
||
dlong adjCost;
|
||
|
||
/* compute adjusted cost */
|
||
adjCost = (dlong)((RoutePath *)(minCostTop->he_id))->rp_togo;
|
||
adjCost = (dlong)(adjCost * mzPenalty.rf_mantissa);
|
||
adjCost = adjCost >> mzPenalty.rf_nExponent;
|
||
adjCost += minCostTop->he_union.hu_dlong;
|
||
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
mzPrintPathHead((RoutePath*)(minCostTop->he_id));
|
||
TxPrintf(" Heap-key adjCost = %.0f\n", (double)adjCost);
|
||
}
|
||
|
||
/* add to adjusted cost heap */
|
||
HeapAddDLong(&mzMinAdjCostHeap, adjCost,
|
||
(char *) (minCostTop->he_id));
|
||
}
|
||
|
||
if(minCostTop!=NULL)
|
||
{
|
||
HeapAddDLong(&mzMinCostHeap,
|
||
minCostTop->he_union.hu_dlong,
|
||
(char *) (minCostTop->he_id));
|
||
}
|
||
}
|
||
|
||
/* Peek at tops of heaps
|
||
* (equal cost elements might have got shuffled above when
|
||
* we placed the last poped element back on the heap.)
|
||
*/
|
||
minAdjCostTop = HeapLookAtTop(&mzMinAdjCostHeap);
|
||
maxToGoTop = HeapLookAtTop(&mzMaxToGoHeap);
|
||
minCostTop = HeapLookAtTop(&mzMinCostHeap);
|
||
|
||
/* Print tops of maxToGo, minCost and minAdjCost heaps */
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("Max togo top:\n");
|
||
if(maxToGoTop)
|
||
{
|
||
mzPrintPathHead((RoutePath*)(maxToGoTop->he_id));
|
||
}
|
||
else
|
||
{
|
||
TxPrintf(" nil\n");
|
||
}
|
||
TxPrintf("Min cost top:\n");
|
||
if(minCostTop)
|
||
{
|
||
mzPrintPathHead((RoutePath*)(minCostTop->he_id));
|
||
}
|
||
else
|
||
{
|
||
TxPrintf(" nil\n");
|
||
}
|
||
TxPrintf("Min adjcost top:\n");
|
||
if(minAdjCostTop)
|
||
{
|
||
TxPrintf(" Heap-key adjCost: %.0f\n",
|
||
(double)(minAdjCostTop->he_union.hu_dlong));
|
||
}
|
||
else
|
||
{
|
||
TxPrintf(" nil\n");
|
||
}
|
||
}
|
||
|
||
if(minCostTop && minAdjCostTop)
|
||
/* need to compare minCostTop and minAdjCostTop */
|
||
{
|
||
/* compute adjusted cost corresponding to current window
|
||
* position (we only penalize for amount toGo exceeding
|
||
* mzWindowMaxToGo)
|
||
*/
|
||
dlong cost, adjCost;
|
||
|
||
cost =(dlong)((RoutePath *)(minAdjCostTop->he_id))->rp_cost;
|
||
adjCost = (dlong)((RoutePath *)(minAdjCostTop->he_id))->rp_togo;
|
||
ASSERT(adjCost >= mzWindowMaxToGo,"mzSearch");
|
||
adjCost -= mzWindowMaxToGo;
|
||
adjCost *= mzPenalty.rf_mantissa;
|
||
adjCost = adjCost >> mzPenalty.rf_nExponent;
|
||
adjCost += cost;
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("WINDOW-CORRECTED ADJCOST: %.0f\n",
|
||
(double)(adjCost));
|
||
}
|
||
if(minCostTop->he_union.hu_dlong <= adjCost)
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("INITIALIZING BLOOM STACK ");
|
||
TxPrintf("WITH TOP OF MIN COST HEAP.\n");
|
||
}
|
||
minCostTop = HeapRemoveTop(&mzMinCostHeap, &buf);
|
||
mzBloomInit((RoutePath *) (minCostTop->he_id));
|
||
}
|
||
else
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("INITIALIZING BLOOM STACK ");
|
||
TxPrintf("WITH TOP OF MIN ADJCOST HEAP.\n");
|
||
}
|
||
minAdjCostTop = HeapRemoveTop(&mzMinAdjCostHeap, &buf);
|
||
mzBloomInit((RoutePath *) (minAdjCostTop->he_id));
|
||
mzNumOutsideBlooms++;
|
||
}
|
||
}
|
||
else if(minCostTop)
|
||
/* minAdjCostHeap empty, so bloom from minCostTop */
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("INITIALIZING BLOOM STACK ");
|
||
TxPrintf("WITH TOP OF MIN COST HEAP.\n");
|
||
}
|
||
minCostTop = HeapRemoveTop(&mzMinCostHeap, &buf);
|
||
mzBloomInit((RoutePath *) (minCostTop->he_id));
|
||
}
|
||
else if(minAdjCostTop)
|
||
/* minCost Heap empty, so bloom from minAdjCostTop */
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("INITIALIZING BLOOM STACK ");
|
||
TxPrintf("WITH TOP OF MIN ADJCOST HEAP.\n");
|
||
}
|
||
minAdjCostTop = HeapRemoveTop(&mzMinAdjCostHeap, &buf);
|
||
mzBloomInit((RoutePath *) (minAdjCostTop->he_id));
|
||
mzNumOutsideBlooms++;
|
||
}
|
||
else
|
||
/* minCost and minAdjCost heaps empty,
|
||
* bloom from top of TOGO heap */
|
||
{
|
||
if(maxToGoTop)
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("INITIALIZING BLOOM STACK ");
|
||
TxPrintf("WITH TOP OF MAX TOGO HEAP.\n");
|
||
}
|
||
maxToGoTop = HeapRemoveTop(&mzMaxToGoHeap, &buf);
|
||
mzBloomInit((RoutePath *) (maxToGoTop->he_id));
|
||
mzNumOutsideBlooms++;
|
||
}
|
||
else
|
||
/* No paths left to extend from */
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("NO PATHS LEFT TO EXTEND FROM.\n");
|
||
}
|
||
morePartialPaths = FALSE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Give final stat report. */
|
||
mzMakeStatReport();
|
||
|
||
/* Return best complete path */
|
||
{
|
||
HeapEntry heEntry;
|
||
|
||
if(HeapRemoveTop(&mzMinCostCompleteHeap,&heEntry))
|
||
{
|
||
if (mzResult)
|
||
{
|
||
if (SigInterruptPending)
|
||
*mzResult = MZ_CURRENT_BEST;
|
||
else
|
||
*mzResult = MZ_SUCCESS;
|
||
}
|
||
return (RoutePath *)(heEntry.he_id);
|
||
}
|
||
else
|
||
{
|
||
if (mzResult)
|
||
{
|
||
if (SigInterruptPending)
|
||
*mzResult = MZ_INTERRUPTED;
|
||
else
|
||
*mzResult = MZ_FAILURE;
|
||
}
|
||
return NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* ----------------------------------------------------------------------------
|
||
*
|
||
* mzExtendPath --
|
||
*
|
||
* Spread out to next interesting point in four directions, and to adjacent
|
||
* layers through contacts.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Adds new routepaths to the queues (ie. heaps and bloomstack).
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
mzExtendPath(path)
|
||
RoutePath *path;
|
||
{
|
||
int extendCode = path->rp_extendCode;
|
||
|
||
if (extendCode & EC_RIGHT)
|
||
{
|
||
mzExtendRight(path);
|
||
}
|
||
|
||
if (extendCode & EC_LEFT)
|
||
{
|
||
mzExtendLeft(path);
|
||
}
|
||
|
||
if (extendCode & EC_UP)
|
||
{
|
||
mzExtendUp(path);
|
||
}
|
||
|
||
if (extendCode & EC_DOWN)
|
||
{
|
||
mzExtendDown(path);
|
||
}
|
||
|
||
if (extendCode & EC_UDCONTACTS)
|
||
{
|
||
mzExtendViaUDContacts(path);
|
||
}
|
||
if (extendCode & EC_LRCONTACTS)
|
||
{
|
||
mzExtendViaLRContacts(path);
|
||
}
|
||
|
||
if (extendCode >= EC_WALKRIGHT)
|
||
{
|
||
if (extendCode & EC_WALKRIGHT)
|
||
{
|
||
mzWalkRight(path);
|
||
}
|
||
else if (extendCode & EC_WALKLEFT)
|
||
{
|
||
mzWalkLeft(path);
|
||
}
|
||
|
||
else if (extendCode & EC_WALKUP)
|
||
{
|
||
mzWalkUp(path);
|
||
}
|
||
|
||
else if (extendCode & EC_WALKDOWN)
|
||
{
|
||
mzWalkDown(path);
|
||
}
|
||
else if (extendCode & EC_WALKUDCONTACT)
|
||
{
|
||
mzWalkUDContact(path);
|
||
}
|
||
else if (extendCode & EC_WALKLRCONTACT)
|
||
{
|
||
mzWalkLRContact(path);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
/*
|
||
* ----------------------------------------------------------------------------
|
||
*
|
||
* mzExtendViaLRContacts --
|
||
*
|
||
* Spread from a point to other planes reachable from it,
|
||
* by using contacts. Stacked contacts are allowed. Search the horizontal
|
||
* block plane. Limit to areas that fit the contact minimum length.
|
||
*
|
||
* Results None.
|
||
*
|
||
* Side effects:
|
||
* Adds RoutePaths to the heap (mzReservePathCostHeap).
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
void
|
||
mzExtendViaLRContacts(path)
|
||
RoutePath *path;
|
||
{
|
||
Point p = path->rp_entry, *lastCpos = NULL;
|
||
RouteLayer *rLayer = path->rp_rLayer;
|
||
RouteContact *rC;
|
||
List *cL;
|
||
RouteLayer *newRLayer;
|
||
Tile *tp;
|
||
TileType type, lastCtype = TT_SPACE;
|
||
RoutePath *spath;
|
||
int bendDist = 0;
|
||
|
||
/* DEBUG - trace calls to this routine */
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
TxPrintf("EXTENDING WITH CONTACTS (HORIZONTAL)\n");
|
||
|
||
/* Check what the last contact was and remember its */
|
||
/* position. Do not allow two contacts of the same */
|
||
/* type within a DRC error distance of each other. */
|
||
|
||
for (spath = path; spath && spath->rp_back && (spath->rp_orient != 'O');
|
||
spath = spath->rp_back);
|
||
if (spath->rp_back)
|
||
{
|
||
lastCpos = &spath->rp_entry;
|
||
rC = MZGetContact(spath, spath->rp_back);
|
||
lastCtype = rC->rc_routeType.rt_tileType;
|
||
}
|
||
|
||
/* Check where the last route bend is. Don't allow a */
|
||
/* contact within a DRC error distance of a bend. */
|
||
|
||
if (path)
|
||
{
|
||
if (path->rp_orient == 'V')
|
||
{
|
||
for (spath = path->rp_back; spath && spath->rp_orient == 'V';
|
||
spath = spath->rp_back);
|
||
if (spath && spath->rp_orient == 'H')
|
||
bendDist = spath->rp_entry.p_y - p.p_y;
|
||
if (bendDist < 0)
|
||
bendDist += rLayer->rl_routeType.rt_width;
|
||
}
|
||
else if (path->rp_orient == 'H')
|
||
{
|
||
for (spath = path->rp_back; spath && spath->rp_orient == 'H';
|
||
spath = spath->rp_back);
|
||
if (spath && spath->rp_orient == 'V')
|
||
bendDist = spath->rp_entry.p_x - p.p_x;
|
||
if (bendDist < 0)
|
||
bendDist += rLayer->rl_routeType.rt_width;
|
||
}
|
||
}
|
||
|
||
/* Loop through contacts that connect to current rLayer */
|
||
for (cL=rLayer->rl_contactL; cL!=NULL; cL=LIST_TAIL(cL))
|
||
{
|
||
rC = (RouteContact *) LIST_FIRST(cL);
|
||
|
||
/* Don't use inactive contacts */
|
||
if (!(rC->rc_routeType.rt_active))
|
||
continue;
|
||
|
||
/* Get "other" route Layer contact connects to */
|
||
if (rC->rc_rLayer1 == rLayer)
|
||
{
|
||
newRLayer = rC->rc_rLayer2;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(rC->rc_rLayer2 == rLayer,
|
||
"mzExtendViaLRContacts");
|
||
newRLayer = rC->rc_rLayer1;
|
||
}
|
||
|
||
/* Don't spread to inactive layers */
|
||
if (!(newRLayer->rl_routeType.rt_active)) continue;
|
||
|
||
/* Find tile on contact plane that contains point. */
|
||
|
||
tp = TiSrPointNoHint(rC->rc_routeType.rt_hBlock, &p);
|
||
type = TiGetType(tp);
|
||
|
||
/* 1. If blocked, don't place a contact */
|
||
if ((type != TT_SPACE) && (type != TT_SAMENODE))
|
||
continue;
|
||
|
||
/* 2. If tile space is not long enough for the contact, don't place */
|
||
if (RIGHT(tp) - p.p_x <= rC->rc_routeType.rt_length
|
||
- rC->rc_routeType.rt_width)
|
||
continue;
|
||
|
||
/* 3. Check distance from last contact, if the same type */
|
||
if (rC->rc_routeType.rt_tileType == lastCtype)
|
||
{
|
||
int cdist = rC->rc_routeType.rt_spacing[lastCtype] +
|
||
rC->rc_routeType.rt_width;
|
||
if ((abs(p.p_x - lastCpos->p_x) < cdist) &&
|
||
(abs(p.p_y - lastCpos->p_y) < cdist))
|
||
continue;
|
||
}
|
||
|
||
/* 4. Check distance to last route bend */
|
||
if (bendDist != 0)
|
||
{
|
||
int cwidth = rC->rc_routeType.rt_width;
|
||
int spacing = rC->rc_routeType.rt_spacing[rLayer->rl_routeType.rt_tileType];
|
||
if (bendDist > cwidth && bendDist < cwidth + spacing)
|
||
continue;
|
||
if (bendDist < 0 && bendDist > -spacing)
|
||
continue;
|
||
}
|
||
|
||
/* OK to drop a contact here */
|
||
{
|
||
dlong conCost;
|
||
int extendCode;
|
||
|
||
/* set contact cost */
|
||
conCost = (dlong) rC->rc_cost;
|
||
|
||
/* determine appropriate extendcode */
|
||
tp = TiSrPointNoHint(newRLayer->rl_routeType.rt_hBlock, &p);
|
||
type = TiGetType(tp);
|
||
|
||
switch (type)
|
||
{
|
||
case TT_SPACE:
|
||
case TT_SAMENODE:
|
||
extendCode = EC_RIGHT | EC_LEFT | EC_UP | EC_DOWN;
|
||
break;
|
||
|
||
case TT_LEFT_WALK:
|
||
extendCode = EC_WALKRIGHT;
|
||
break;
|
||
|
||
case TT_RIGHT_WALK:
|
||
extendCode = EC_WALKLEFT;
|
||
break;
|
||
|
||
case TT_TOP_WALK:
|
||
extendCode = EC_WALKDOWN;
|
||
break;
|
||
|
||
case TT_BOTTOM_WALK:
|
||
extendCode = EC_WALKUP;
|
||
break;
|
||
|
||
case TT_ABOVE_LR_WALK:
|
||
case TT_BELOW_LR_WALK:
|
||
/* TO DO: Check if stacked contacts are allowed! */
|
||
extendCode = EC_WALKLRCONTACT;
|
||
break;
|
||
|
||
case TT_ABOVE_UD_WALK:
|
||
case TT_BELOW_UD_WALK:
|
||
/* TO DO: Check if stacked contacts are allowed! */
|
||
extendCode = EC_WALKUDCONTACT;
|
||
break;
|
||
|
||
case TT_DEST_AREA:
|
||
|
||
extendCode = EC_COMPLETE;
|
||
break;
|
||
|
||
default:
|
||
/* this shouldn't happen */
|
||
ASSERT(FALSE,"mzExtendViaLRContacts");
|
||
continue;
|
||
}
|
||
|
||
/* Finally add point after contact */
|
||
mzAddPoint(path, &p, newRLayer, 'O', extendCode, &conCost);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* ----------------------------------------------------------------------------
|
||
*
|
||
* mzExtendViaUDContacts --
|
||
*
|
||
* Spread from a point to other planes reachable from it,
|
||
* by using contacts. Stacked contacts are allowed. Search the vertical
|
||
* block plane. Limit to areas that fit the contact minimum length.
|
||
*
|
||
* Results None.
|
||
*
|
||
* Side effects:
|
||
* Adds RoutePaths to the heap (mzReservePathCostHeap).
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
void
|
||
mzExtendViaUDContacts(path)
|
||
RoutePath *path;
|
||
{
|
||
Point p = path->rp_entry, *lastCpos = NULL;
|
||
RouteLayer *rLayer = path->rp_rLayer;
|
||
RouteContact *rC;
|
||
List *cL;
|
||
RouteLayer *newRLayer;
|
||
Tile *tp;
|
||
TileType type, lastCtype = TT_SPACE;
|
||
RoutePath *spath;
|
||
int bendDist = 0;
|
||
|
||
/* DEBUG - trace calls to this routine */
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
TxPrintf("EXTENDING WITH CONTACTS (VERTICAL)\n");
|
||
|
||
/* Check what the last contact was and remember its */
|
||
/* position. Do not allow two contacts of the same */
|
||
/* type within a DRC error distance of each other. */
|
||
|
||
for (spath = path; spath && spath->rp_back && (spath->rp_orient != 'X');
|
||
spath = spath->rp_back);
|
||
if (spath->rp_back)
|
||
{
|
||
lastCpos = &spath->rp_entry;
|
||
rC = MZGetContact(spath, spath->rp_back);
|
||
lastCtype = rC->rc_routeType.rt_tileType;
|
||
}
|
||
|
||
/* Check where the last route bend is. Don't allow a */
|
||
/* contact within a DRC error distance of a bend. */
|
||
|
||
if (path)
|
||
{
|
||
if (path->rp_orient == 'V')
|
||
{
|
||
for (spath = path->rp_back; spath && spath->rp_orient == 'V';
|
||
spath = spath->rp_back);
|
||
if (spath && spath->rp_orient == 'H')
|
||
bendDist = spath->rp_entry.p_y - p.p_y;
|
||
if (bendDist < 0)
|
||
bendDist += rLayer->rl_routeType.rt_width;
|
||
}
|
||
else if (path->rp_orient == 'H')
|
||
{
|
||
for (spath = path->rp_back; spath && spath->rp_orient == 'H';
|
||
spath = spath->rp_back);
|
||
if (spath && spath->rp_orient == 'V')
|
||
bendDist = spath->rp_entry.p_x - p.p_x;
|
||
if (bendDist < 0)
|
||
bendDist += rLayer->rl_routeType.rt_width;
|
||
}
|
||
}
|
||
|
||
/* Loop through contacts that connect to current rLayer */
|
||
for (cL=rLayer->rl_contactL; cL!=NULL; cL=LIST_TAIL(cL))
|
||
{
|
||
rC = (RouteContact *) LIST_FIRST(cL);
|
||
|
||
/* Don't use inactive contacts */
|
||
if (!(rC->rc_routeType.rt_active))
|
||
continue;
|
||
|
||
/* Get "other" route Layer contact connects to */
|
||
if (rC->rc_rLayer1 == rLayer)
|
||
{
|
||
newRLayer = rC->rc_rLayer2;
|
||
}
|
||
else
|
||
{
|
||
ASSERT(rC->rc_rLayer2 == rLayer,
|
||
"mzExtendViaUDContacts");
|
||
newRLayer = rC->rc_rLayer1;
|
||
}
|
||
|
||
/* Don't spread to inactive layers */
|
||
if (!(newRLayer->rl_routeType.rt_active)) continue;
|
||
|
||
/* Find tile on contact plane that contains point. */
|
||
|
||
tp = TiSrPointNoHint(rC->rc_routeType.rt_vBlock, &p);
|
||
type = TiGetType(tp);
|
||
|
||
/* 1. If blocked, don't place a contact */
|
||
if ((type != TT_SPACE) && (type != TT_SAMENODE))
|
||
continue;
|
||
|
||
/* 2. If tile space is not long enough for the contact, don't place */
|
||
if (TOP(tp) - p.p_y <= rC->rc_routeType.rt_length
|
||
- rC->rc_routeType.rt_width)
|
||
continue;
|
||
|
||
/* 3. Check distance from last contact, if the same type */
|
||
if (rC->rc_routeType.rt_tileType == lastCtype)
|
||
{
|
||
int cdist = rC->rc_routeType.rt_spacing[lastCtype] +
|
||
rC->rc_routeType.rt_width;
|
||
if ((abs(p.p_x - lastCpos->p_x) < cdist) &&
|
||
(abs(p.p_y - lastCpos->p_y) < cdist))
|
||
continue;
|
||
}
|
||
|
||
/* 4. Check distance to last route bend */
|
||
if (bendDist != 0)
|
||
{
|
||
int cwidth = rC->rc_routeType.rt_width;
|
||
int spacing = rC->rc_routeType.rt_spacing[rLayer->rl_routeType.rt_tileType];
|
||
if (bendDist > cwidth && bendDist < cwidth + spacing)
|
||
continue;
|
||
if (bendDist < 0 && bendDist > -spacing)
|
||
continue;
|
||
}
|
||
|
||
/* OK to drop a contact here */
|
||
{
|
||
dlong conCost;
|
||
int extendCode;
|
||
|
||
/* set contact cost */
|
||
conCost = (dlong) rC->rc_cost;
|
||
|
||
/* determine appropriate extendcode */
|
||
tp = TiSrPointNoHint(newRLayer->rl_routeType.rt_vBlock, &p);
|
||
type = TiGetType(tp);
|
||
|
||
switch (type)
|
||
{
|
||
case TT_SPACE:
|
||
case TT_SAMENODE:
|
||
extendCode = EC_RIGHT | EC_LEFT | EC_UP | EC_DOWN;
|
||
break;
|
||
|
||
case TT_LEFT_WALK:
|
||
extendCode = EC_WALKRIGHT;
|
||
break;
|
||
|
||
case TT_RIGHT_WALK:
|
||
extendCode = EC_WALKLEFT;
|
||
break;
|
||
|
||
case TT_TOP_WALK:
|
||
extendCode = EC_WALKDOWN;
|
||
break;
|
||
|
||
case TT_BOTTOM_WALK:
|
||
extendCode = EC_WALKUP;
|
||
break;
|
||
|
||
case TT_ABOVE_LR_WALK:
|
||
case TT_BELOW_LR_WALK:
|
||
/* TO DO: Check if stacked contacts are allowed! */
|
||
extendCode = EC_WALKLRCONTACT;
|
||
break;
|
||
|
||
case TT_ABOVE_UD_WALK:
|
||
case TT_BELOW_UD_WALK:
|
||
/* TO DO: Check if stacked contacts are allowed! */
|
||
extendCode = EC_WALKUDCONTACT;
|
||
break;
|
||
|
||
case TT_DEST_AREA:
|
||
|
||
extendCode = EC_COMPLETE;
|
||
break;
|
||
|
||
default:
|
||
/* this shouldn't happen */
|
||
ASSERT(FALSE,"mzExtendViaUDContacts");
|
||
continue;
|
||
}
|
||
|
||
/* Finally add point after contact */
|
||
mzAddPoint(path, &p, newRLayer, 'X', extendCode, &conCost);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* ----------------------------------------------------------------------------
|
||
*
|
||
* mzAddPoint --
|
||
*
|
||
* Process interesting point. If point within bounds and not redundant,
|
||
* link to previous path, update cost, and add to heap.
|
||
*
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Adds a RoutePath to the heap.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
void
|
||
mzAddPoint(path, p, rLayer, orient, extendCode, costptr)
|
||
RoutePath *path; /* path that new point extends */
|
||
Point *p; /* new point */
|
||
RouteLayer *rLayer; /* Route Layer of new point */
|
||
int orient; /* 'H' = endpt of hor seg, 'V' = endpt of vert seg,
|
||
* 'O' = LR contact, 'X' = UD contact,
|
||
* 'B' = first point in path and blocked
|
||
*/
|
||
int extendCode; /* interesting directions to extend in */
|
||
dlong *costptr; /* Incremental cost of new path segment */
|
||
|
||
{
|
||
RoutePath *newPath;
|
||
RoutePath *hashedPath;
|
||
HashEntry *he;
|
||
PointKey pk;
|
||
dlong togo, cost;
|
||
|
||
/* DEBUG - print candidate frontier point */
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("mzAddPoint called: point=(%d,%d), layer=%s, orient='%c'\n",
|
||
p->p_x, p->p_y,
|
||
DBTypeLongNameTbl[rLayer->rl_routeType.rt_tileType],
|
||
orient);
|
||
}
|
||
|
||
cost = *costptr;
|
||
ASSERT(cost >= 0,"mzAddPoint");
|
||
|
||
/* Ignore if outside of bounding box */
|
||
if (!GEO_ENCLOSE(p, &mzBoundingRect))
|
||
return;
|
||
|
||
/* get estimated distance togo */
|
||
if(extendCode == EC_COMPLETE)
|
||
togo = 0;
|
||
else
|
||
togo = mzEstimatedCost(p);
|
||
|
||
/* compute (total) cost for new path */
|
||
{
|
||
/* initially cost = cost of new leg in path */
|
||
|
||
/* Add jogcost if appropriate */
|
||
|
||
if (path != NULL &&
|
||
path->rp_rLayer == rLayer &&
|
||
path->rp_orient != 'O' && path->rp_orient != 'X' &&
|
||
path->rp_orient != orient)
|
||
{
|
||
cost += rLayer->rl_jogCost;
|
||
}
|
||
|
||
/* Add estimated total cost prior to new point,
|
||
* (or cost so far in the case of initial paths).
|
||
*/
|
||
if (path != NULL)
|
||
{
|
||
cost += path->rp_cost;
|
||
}
|
||
/* If not initial path, subtract out old estimated cost togo */
|
||
if (mzPathSource != SOURCE_INIT)
|
||
{
|
||
cost -= path->rp_togo;
|
||
}
|
||
|
||
/* Add new estimated cost to completion */
|
||
cost += togo;
|
||
}
|
||
|
||
/* Check hash table to see if cheaper path to this point already
|
||
* found - if so don't add this point.
|
||
*/
|
||
pk.pk_point = *p;
|
||
pk.pk_rLayer = rLayer;
|
||
pk.pk_orient = orient;
|
||
#if SIZEOF_VOID_P == 8
|
||
pk.pk_buffer = 0; /* Otherwise the hash function screws up */
|
||
#endif
|
||
|
||
he = HashFind(&mzPointHash, (char *) &pk);
|
||
hashedPath = (RoutePath *) HashGetValue(he);
|
||
|
||
if (hashedPath != NULL && (hashedPath->rp_cost <= cost))
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("new point NOT added, at least as good "
|
||
"path to pt already exists: ");
|
||
TxPrintf("new cost = %.0f, ",
|
||
(double)(cost));
|
||
TxPrintf("cheapest cost = %.0f\n",
|
||
(double)(hashedPath->rp_cost));
|
||
}
|
||
return;
|
||
}
|
||
|
||
/* If initial path, update min initial cost */
|
||
if(mzPathSource==SOURCE_INIT)
|
||
{
|
||
if(cost < mzMinInitialCost)
|
||
{
|
||
mzMinInitialCost = cost;
|
||
}
|
||
}
|
||
|
||
/* Create new path element */
|
||
newPath = NEWPATH();
|
||
newPath->rp_rLayer = rLayer;
|
||
newPath->rp_entry = *p;
|
||
newPath->rp_orient = orient;
|
||
newPath->rp_cost = cost;
|
||
newPath->rp_extendCode = extendCode;
|
||
newPath->rp_togo = togo;
|
||
newPath->rp_back = path;
|
||
|
||
/* keep statistics */
|
||
mzNumPathsGened++;
|
||
|
||
/* Enter in hash table */
|
||
HashSetValue(he, (ClientData) newPath);
|
||
|
||
/* Add to appropriate queue or stack */
|
||
if(extendCode == EC_COMPLETE)
|
||
{
|
||
if (DebugIsSet(mzDebugID, mzDebMaze))
|
||
{
|
||
TxPrintf("PATH COMPLETE (WALKED IN). Add to complete heap.\n");
|
||
}
|
||
|
||
HeapAddDLong(&mzMinCostCompleteHeap, newPath->rp_cost,
|
||
(char *) newPath);
|
||
|
||
/* compute stats and make completed path report */
|
||
{
|
||
mzNumComplete++;
|
||
|
||
if(mzVerbosity>=VERB_STATS)
|
||
{
|
||
dlong cost, excessCost;
|
||
double excessPercent;
|
||
|
||
mzMakeStatReport();
|
||
|
||
TxPrintf("PATH #%d ", mzNumComplete);
|
||
|
||
|
||
cost = newPath->rp_cost;
|
||
TxPrintf("cst:%.0f, ", (double)(newPath->rp_cost));
|
||
if(cost < mzInitialEstimate)
|
||
{
|
||
TxPrintf("(<est)");
|
||
}
|
||
else
|
||
{
|
||
excessCost = cost - mzInitialEstimate;
|
||
excessPercent = 100.0 * (double)(excessCost)/
|
||
(double)(mzInitialEstimate);
|
||
|
||
TxPrintf("overrun: %.0f%%", excessPercent);
|
||
}
|
||
|
||
TxPrintf("\n");
|
||
}
|
||
}
|
||
}
|
||
else if(extendCode >= EC_WALKRIGHT)
|
||
{
|
||
LIST_ADD(newPath, mzWalkStack);
|
||
}
|
||
else
|
||
{
|
||
switch(mzPathSource)
|
||
{
|
||
case SOURCE_BLOOM:
|
||
if(orient=='O')
|
||
{
|
||
/* just changing layers, add back to bloom */
|
||
LIST_ADD(newPath, mzBloomStack);
|
||
}
|
||
else if((orient=='H' && rLayer->rl_hCost<=rLayer->rl_vCost) ||
|
||
(orient=='V' && rLayer->rl_vCost<=rLayer->rl_hCost))
|
||
{
|
||
/* going in preferred direction */
|
||
LIST_ADD(newPath, mzStraightStack);
|
||
}
|
||
else
|
||
{
|
||
/* non preferred, add to heaps */
|
||
HeapAddDLong(&mzMaxToGoHeap, togo, (char *) newPath);
|
||
}
|
||
break;
|
||
|
||
case SOURCE_STRAIGHT:
|
||
if(path->rp_orient==orient && (cost < mzBloomMaxCost))
|
||
{
|
||
/* straight and within bounds, keep going*/
|
||
LIST_ADD(newPath, mzStraightStack);
|
||
}
|
||
else
|
||
{
|
||
/* from here on, follow downhill only */
|
||
LIST_ADD(newPath, mzDownHillStack);
|
||
}
|
||
break;
|
||
|
||
case SOURCE_DOWNHILL:
|
||
{
|
||
dlong oldCostPlusOne;
|
||
|
||
oldCostPlusOne = path->rp_cost + 1;
|
||
if(cost <oldCostPlusOne)
|
||
{
|
||
/* cost within a unit, keep following down hill */
|
||
LIST_ADD(newPath, mzDownHillStack);
|
||
}
|
||
else
|
||
{
|
||
/* increased cost, add to heaps */
|
||
HeapAddDLong(&mzMaxToGoHeap, togo, (char *) newPath);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SOURCE_INIT:
|
||
/* Initial points go on heap */
|
||
HeapAddDLong(&mzMaxToGoHeap, togo, (char *) newPath);
|
||
break;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
/*
|
||
* ----------------------------------------------------------------------------
|
||
*
|
||
* mzBloomInit --
|
||
*
|
||
* Initialize bloom stack with given path.
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Adds a routepath to bloom stack, and sets mzBloomMaxCost.
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
void
|
||
mzBloomInit(path)
|
||
RoutePath *path; /* path that new point extends */
|
||
{
|
||
ASSERT(mzBloomStack==NULL,"mzBloomInit");
|
||
|
||
LIST_ADD(path, mzBloomStack);
|
||
mzBloomMaxCost = path->rp_cost + mzBloomDeltaCost;
|
||
|
||
mzNumBlooms++;
|
||
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* ----------------------------------------------------------------------------
|
||
*
|
||
* mzMakeStatReport --
|
||
*
|
||
* Print out route statistics
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side effects:
|
||
* Output via TxPrintf()
|
||
*
|
||
* ----------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
mzMakeStatReport()
|
||
{
|
||
|
||
/* if we aren't being verbose, skip this */
|
||
if(mzVerbosity<VERB_STATS) return;
|
||
|
||
TxPrintf(" Blms:%d(%d)", mzNumBlooms - mzNumOutsideBlooms,
|
||
mzNumBlooms);
|
||
|
||
TxPrintf(" Wndw:%.0f(%.0f%%)", (double)(mzWindowMaxToGo),
|
||
(100.0 * (1- (double)(mzWindowMaxToGo)/
|
||
((double)(mzInitialEstimate) +(double)(mzWWidth)))));
|
||
|
||
TxPrintf(" Pts:%d(%d)",
|
||
mzNumPaths,
|
||
mzNumPathsGened);
|
||
|
||
TxPrintf(" Blkgen: %dx%.0f",
|
||
mzBlockGenCalls,
|
||
mzBlockGenArea/mzBlockGenCalls);
|
||
|
||
TxPrintf("(%.0f/icst)",
|
||
(double)mzBlockGenArea/(double)(mzInitialEstimate));
|
||
|
||
TxPrintf("\n");
|
||
return;
|
||
}
|
||
|