pathfind.t

documentation
#charset "us-ascii"
#include "advlite.h"

/* Abstract pathfinder */
class Pathfinder: object
    
    /* 
     *   When populated the pathsFound will contain a Vector of path Vectors,
     *   each path Vector comprising a series of two element lists, the first
     *   element describing the route taken and the second the destination
     *   arrived at (e.g. [northDir, hall] meaning go north to reach the hall).
     */
    
    pathsFound = nil
    
    /* 
     *   The number of steps we have tried so far. We start with 1, being the
     *   null step to our starting point.
     */
    steps = 1
    
    /* 
     *   A Vector containing all the nodes we have visited so far in our attempt
     *   to find a route. This enables us to cull paths that lead somewhere
     *   we've already been.
     */
    
    nodesVisited = nil
    
    
    findPath(start, target)
    {
        /* 
         *   Initiate the search by setting up the Vectors we need and
         *   populating them with the null route to our starting point.
         */
            
        cachedRoute = nil;
        currentDestination = target;
        
        pathsFound = new Vector(20);
        nodesVisited = new Vector(20);
        local newPath = new Vector(2);
        newPath.append([nil, start]);
        pathsFound.append(newPath);
        nodesVisited.append(start);
        steps = 1;
        if(start == target)
            return newPath;
        
        
        /* 
         *   To find the path we take a step out from our starting point through
         *   all available routes. We note the route we took and where we
         *   arrived at as a set of new paths building on our existing paths. We
         *   then discard all paths that are shorter than the number of steps we
         *   have now taken and look for one among the remainder that arrives at
         *   our target destination. If we find one, we return it. If not, we
         *   remove all paths that lead to destinations we have visited before,
         *   and then try taking another step, noting the destinations to which
         *   it leads. Repeat until we either find a path to our target or we
         *   run out of new paths to try.
         */
        
        while(pathsFound.length > 0)
        {
            takeOneStep();
            
            /* cull all paths that are shorter than steps long */
            
            pathsFound = pathsFound.subset({x: x.length == steps});
            
            /* see if any of the paths we've found lead to our target */
            local pathFound = pathsFound.valWhich({x: x[steps][2] == target} );
            if(pathFound != nil)
            {
                cachedRoute = pathFound;
                return pathFound;
            }
            
            /* remove all paths that end in nodes we've already visited */
            pathsFound = pathsFound.subset({x: nodesVisited.indexOf(x[steps][2])
                                           == nil});
                
                /* note which nodes have now been visited */
                
            foreach(local cur in pathsFound)
                nodesVisited.append(cur[steps][2]);
            
            
         }
        
        return nil;
    }
    
    takeOneStep()
    {
        /* Note that we've taken another step out from our starting point */
        steps ++;
        
        /* 
         *   Copy the existing paths into a temporary Vector, since we're about
         *   to add to them and we only want to iterate over the existing list.
         */
        local temp = new Vector(pathsFound);
        
        /* 
         *   For each existing route, see what happens if we advance one more
         *   step in every available direction and add the new routes to our
         *   list of paths.
         */
        foreach(local cur in temp)
            findDestinations(cur);
    }
    
    /* Find all the destinations one step away from cur */
    findDestinations(cur)
    {
        /* Specific instances must define how this is done */
    }
    
    /* The most recently calculated route */
    cachedRoute = nil
    
    /* The destination of the most recently calculated route. */
    currentDestination = nil
    
;

/* 
 *   A Pathfinder specialized for finding a route through the game map. Note
 *   that this can only find a route through TravelConnector objects (which
 *   includes direction properties attached to Rooms, Doors and other
 *   TravelConnectors).
 */
routeFinder: Pathfinder    
    
    findDestinations(cur)
    {
        /* Note the location our current path leads to */
        local loc = cur[steps - 1][2];
                
        
        /* See what leads in every available direction from this location */
        for(local dir = firstObj(Direction); dir != nil ; dir = nextObj(dir,
            Direction))
        {
            local newPath = new Vector(cur);
            
            /* 
             *   If the direction property points to an object, see if it points
             *   to a valid path.
             */
            if(loc.propType(dir.dirProp) == TypeObject)
            {
                local obj = loc.(dir.dirProp);
                
                /* 
                 *   if the object is a locked door and we want to exclude
                 *   locked doors, or if there's some other reason the actor
                 *   cannot pass this way, we can't use this path.
                 */
                
                if(excludeLockedDoors &&
                    (obj.isLocked 
                      || obj.canTravelerPass(gActor) == nil
                      || valToList(obj.travelBarriers).indexWhich(
                          { b: !b.canTravelerPass(gActor, obj)}) != nil))
                    return;
                
                
                /* 
                 *   If it leads to a non-nil destination note the path to this
                 *   object. This will be the path that got us to this location
                 *   plus the one additional step.
                 */    
                local dest = loc.(dir.dirProp).getDestination(loc);
                if(dest != nil)
                {
                    newPath.append([dir, dest]);
                    pathsFound.append(newPath);
                }
            }
            
            /*  
             *   if the direction property points to code, see if it provides a
             *   valid path.
             */
            
            if(loc.propType(dir.dirProp) == TypeCode)
            {
                /* first look up the destination this code takes the actor to */
                local dest = libGlobal.extraDestInfo[[loc, dir]];
                
                /* 
                 *   the destination is only of interest if it's not nowhere,
                 *   the default unknown destination, or the location we're
                 *   trying to leave.
                 *
                 *
                 *   if it's none of these, add it to the list of possible paths
                 *
                 */
                if(dest not in (nil, loc, unknownDest_, varDest_))                   
                {
                    newPath.append([dir, dest]);
                    pathsFound.append(newPath);
                }
                
            }
            
        }
    }   
    
    excludeLockedDoors = true
;

/* 
 *   The pcRouteFinder works exactly the same as the more general routeFinder
 *   except that it finds routes only through TravelConnectors whose
 *   destinations are known.
 */
pcRouteFinder: Pathfinder
    findDestinations(cur)
    {
        /* Note the location our current path leads to */
        local loc = cur[steps - 1][2];
               
        /* See what leads in every available direction from this location */
        for(local dir = firstObj(Direction); dir != nil ; dir = nextObj(dir,
            Direction))
        {
            local newPath = new Vector(cur);
            
            /* 
             *   If the direction property points to an object, see if it points
             *   to a valid path.
             */
            if(loc.propType(dir.dirProp) == TypeObject)                
            {
                local conn = loc.(dir.dirProp);
                
                /* 
                 *   If it leads to a non-nil destination that the pc knowns,
                 *   note the path to this object. This will be the path that
                 *   got us to this location plus the one additional step.
                 */    
                local dest = conn.getDestination(loc);
                
                /* 
                 *   if both the location (loc) and the destination (dest) lie
                 *   in the same familiar region, then assume the pc knows
                 *   his/her way between the two rooms and so set
                 *   isDestinationKnown to true
                 */
                
                if(!conn.isDestinationKnown && 
                   loc.regionsInCommonWith(dest).indexWhich(
                       {x: x.familiar}) != nil)
                    conn.isDestinationKnown = true;
                
                /* 
                 *   if the connector leads to a known destination then add the
                 *   direction and its destination to a new path
                 */
                
                if(dest != nil && conn.isDestinationKnown)
                {
                    newPath.append([dir, dest]);
                    pathsFound.append(newPath);
                }
            }
            /*  
             *   if the direction property points to code, see if it provides a
             *   valid path.
             */
            
            if(loc.propType(dir.dirProp) == TypeCode)
            {
                /* first look up the destination this code takes the actor to */
                local dest = libGlobal.extraDestInfo[[loc, dir]];
                
                /* 
                 *   the destination is only of interest if it's not nowhere,
                 *   the default unknown destination, or the location we're
                 *   trying to leave.
                 *
                 *
                 *   if it's none of these, add it to the list of possible paths
                 *   (The fact that it's none of these implies that the
                 *   destination is known so we don't need to apply any further
                 *   tests to check that).
                 *
                 */
                if(dest not in (nil, loc, unknownDest_, varDest_))                                      
                {
                    newPath.append([dir, dest]);
                    pathsFound.append(newPath);
                }                
            }
        }  
        
    }
;
Adv3Lite Library Reference Manual
Generated on 01/03/2024 from adv3Lite version 1.6.2