Intricate PathFinder usage problems.



  • Explanation

    I recently decided that I needed a good (read generic) bit of code to handle all my path finding needs.  I needed something I could reuse under multiple circumstances and with various types of creeps.  One other major requirement was that the code would need to be able to take into account any rooms I designate as "no-fly" zones so as not to send a defenseless creep right into my neighbor's well-defended room just because it was on the shortest path between its current location and destination room.

    I resolved on an approach that involved creating a new module that I called "utils" and importing that code where ever I needed it by way of the usual "require('utils')" statement. I then added a similar module for "settings" which contains, among other things, my list of off-limits rooms as an array variable.

    I spent a great deal of time trying to figure out the mechanics of the PathFinder module and, using the example code in the api documentation as a basis, was able to come up with the following function:

    function(fromPos, toPos) {
    var settings = require('settings');

    let allowedRooms = {
    [fromPos.roomName]: true
    };

    PathFinder.use(true);
    let route = Game.map.findRoute(fromPos.roomName, toPos.roomName, {
    routeCallback(roomName) {
    let parsed = /^[WE]([0-9]+)[NS]([0-9]+)$/.exec(roomName);
    let isHighway = (parsed[1] % 10 === 0) || (parsed[2] % 10 === 0);

    if (isHighway) {
    return 1;
    } else if (settings.avoidedRooms.includes(roomName)) {
    return Infinity;
    } else {
    return 1.5;
    }
    }
    });

    // Didn't find a path!
    if (route == ERR_NO_PATH) {
    console.log(`Error finding path to ${toPos.roomName} from ${fromPos.roomName}`);
    return;
    }

    route.forEach(function(info) {
    allowedRooms[info.room] = true;
    });

    // Invoke PathFinder, allowing access only to rooms from `findRoute`
    let ret = PathFinder.search(fromPos, toPos, {
    plainCost: 1,
    swampCost: 3,
    roomCallback(roomName) {
    if (allowedRooms[roomName] === undefined) {
    return false;
    }
    let room = Game.rooms[roomName];
    // In this example `room` will always exist, but since PathFinder
    // supports searches which span multiple rooms you should be careful!
    if (!room) return;

    let costs = new PathFinder.CostMatrix;

    room.find(FIND_STRUCTURES).forEach(function(structure) {
    if (structure.structureType === STRUCTURE_ROAD) {
    // Favor roads over plain tiles
    costs.set(structure.pos.x, structure.pos.y, 1);
    } else if (structure.structureType !== STRUCTURE_CONTAINER &&
    (structure.structureType !== STRUCTURE_RAMPART ||
    !structure.my)) {
    // Can't walk through non-walkable buildings
    costs.set(structure.pos.x, structure.pos.y, 0xff);
    }
    });
    return costs;
    }
    });

    return ret.path;
    }

    My settings module contains the following pertinent code:
    avoidedRooms: [
    'E8N48',
    'E9N47',
    'E7N47'
    ]

    Problem

    The result of the call I'm making to this function is being stored in my creep's memory under the name "path".  I'm not serializing it or anything. When I want to use the path to navigate I'm calling creep.moveByPath(creep.memory.path).  Unfortunately, it always results in ERR_NOT_FOUND.  From what I've been able to see when I inspect the contents of the path using JSON.stringify() on the console, it appears that the first element of the path is always an adjacent location to the creep and looks like this:

     [
    {
    "roomName": "E9N48",
    "x": 25,
    "y": 36
    },
    {
    "roomName": "E9N48",
    "x": 26,
    "y": 35
    },
    {
    "roomName": "E9N48",
    "x": 27,
    "y": 34
    },
    {
    "roomName": "E9N48",
    "x": 28,
    "y": 34
    },
    .
    .
    .
    {
    "roomName": "E6N49",
    "x": 23,
    "y": 13
    },
    {
    "roomName": "E6N49",
    "x": 22,
    "y": 14
    },
    {
    "roomName": "E6N49",
    "x": 22,
    "y": 15
    },
    {
    "roomName": "E6N49",
    "x": 22,
    "y": 16
    }
    ]

    What am I doing wrong here?  And, what other information would help in pinpointing the problem?

  • Culture

    You need to convert the deserialized path to an array of RoomPosition objects in order for moveByPath to understand it.