npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

emyrk-screeps-cartographer

v1.9.1

Published

An advanced (and open source) movement library for Screeps

Downloads

15

Readme

docs

screeps-cartographer

Cartographer is an advanced (and open source) movement library for Screeps.

Features

  • Flexible caching of paths in heap, Memory, or (eventually) segments
  • Path to the closest of multiple targets
  • Fully configurable to fit your needs
  • Can trigger repathing when creeps are stuck
  • Can cache custom paths for road-building or reuse with Cartographer's moveByPath
  • Can set movement priorities to allow higher-priority minions to path before lower-priority minions
  • Traffic management moves stationary minions out of the way of moving ones, while allowing them to keep range to a target
  • Enhanced findRoute reduces PathFinder search space with intelligent heuristics to produce optimal paths
  • Dynamically re-routes around specific targets with minimal repathing
  • Automatically tracks and paths through intrashard (but not intershard) portals.

Roadmap

  • [x] Replacement for stock moveTo with configurable caching and full PathFinder options
  • [x] Point-of-interest path caching for remotes & local economy movement
  • [x] Traffic management with creep prioritization & shoving
  • [x] Long-distance travel with better paths than Game.map.findRoute yields

Using Cartographer

For an example, see src/tests/index.ts.

Setup

Call preTick() at the beginning of your loop, and (to enable traffic management) call reconcileTraffic() at the end of it:

import { preTick } from 'screeps-cartographer';
const loop = () => {
  preTick();
  // your code goes here
  reconcileTraffic();
};

Basic Movement

Call moveTo(), passing in a creep and the target:

// target with .pos
moveTo(creep, creep.room.storage)
// target room position
moveTo(creep, new RoomPosition(20, 24, 'W2N5'))
// target room position with range
moveTo(creep, { pos: new RoomPosition(25, 25, 'W2N5'), range: 10 })
// list of room positions (will move to closest)
moveTo(creep, [
  new RoomPosition(20, 20, 'W2N5'),
  new RoomPosition(20, 21, 'W2N5'),
  new RoomPosition(20, 22, 'W2N5'),
  new RoomPosition(20, 23, 'W2N5'),
])
// list of room positions with range (will move to closest)
moveTo(creep, [
  { pos: new RoomPosition(20, 20, 'W2N5'), range: 3 }
  { pos: new RoomPosition(20, 23, 'W2N5'), range: 3 }
])

You can also pass in options, including some custom flags and anything supported by PathFinder options.

// flee from target
moveTo(creep, { pos: hostileCreep.pos, range: 3 }, { flee: true });
// repath after a certain number of ticks, like stock moveTo
moveTo(creep, creep.room.storage, { reusePath: 5 });
// path around creeps (default is false)
moveTo(creep, creep.room.storage, { avoidCreeps: true });
// set repath interval when creep is stuck, and fallback
// options for the repath
moveTo(creep, creep.room.storage, { avoidCreeps: false, repathIfStuck: 5 }, { avoidCreeps: true });
// don't path around structures (default is true)
moveTo(creep, creep.room.storage, { avoidObstacleStructures: false });
// set custom terrain values
moveTo(creep, creep.room.storage, { roadCost: 5, plainCost: 1, swampCost: 1 });

You can find the full list of extra options here.

Using Cached Paths

Instead of calling moveTo each time, you may find it more efficient to save a cached path and reuse it for multiple creeps. One common example would be pathing between storage and remote sources.

// create initial path to remote source
const path1 = cachePath(storage.room.name + source.id + '1', storage.pos, { pos: source.pos, range: 1 });
const harvestPos = path1[path1.length - 1];
// create secondary path, avoiding the road, for empty haulers
cachePath(
  storage.room.name + source.id + '2',
  storage.pos,
  { pos: path1[path1.length - 2], range: 0 }, // rejoin the first path just before the harvester
  {
    roadCost: 1,
    plainCost: 1,
    swampCost: 1,
    roomCallback(room) {
      const cm = new PathFinder.CostMatrix();
      if (room === harvestPos.roomName) {
        // harvest pos is not pathable because a creep will be here
        cm.set(harvestPos.x, harvestPos.y, 255);
      }
      for (const pos of path1) {
        if (pos.roomName === room) cm.set(pos.x, pos.y, 50);
      }
      return cm;
    }
  }
);

// build roads
const path = getCachedPath(storage.room.name + source.id + '1');
path.forEach(pos => pos.createConstructionSite(STRUCTURE_ROAD));

// move to remote source
moveByPath(haulerCreep, storage.room.name + source.id + '2');
// return home from remote source, following path
moveByPath(haulerCreep, storage.room.name + source.id + '1', { reverse: true });

Traffic Management

Traffic management can be enabled by simply including the reconcileTraffic function in your main loop, after all creep movement has been requested:

export const loop = () => {
  runCreepLogic();
  reconcileTraffic();
};

If reconcileTraffic is not included, creeps will simply default to unmanaged movement. Be careful to run potentially expensive operations after reconcileTraffic to avoid running out of bucket - your creeps will not move if you run out of CPU in a tick before reconcileTraffic gets a chance to run.

reconcileTraffic will only manage creeps that use Cartographer to move; it is not compatible with Creep prototype move methods, so will cause issues if you try to mix both approaches. It's best to pick one or the other for all creep movement.

If you have a specialized use case (e.g. quads/duos) and need low-level movement control, you can invoke the Cartographer-provided move() function to move a creep and track it for traffic management.

Finally, you can use the blockSquare() function to push creeps out of a square even if nothing else is moving there (for clearing space at spawn, or to lay down a construction site, for example).

Setting Movement Priorities

All move functions accept a priority option:

moveTo(creep, controller, { priority: 10 });

Creeps with a higher priority will be given preference over creeps with a lower priority: if both want to path to the same square on a given tick, the one with a higher priority will move and the one with a lower priority will not (also saving its intent cost).

Long-Range Pathing

Cartographer uses the builtin findRoute as a starting point, but the shortest route (by room count) is not always the best path (by tiles traversed). With some intelligent heuristics, it adds a few rooms to the route that may allow PathFinder to take a shortcut. PathFinder's search is then constrained to those selected rooms.

To control the selected route, you can set weights for types of rooms:

moveTo(
  creep,
  { pos: new RoomPosition(25, 25, 'W20N20'), range: 20 },
  {
    defaultRoomCost: 1,
    sourceKeeperRoomCost: 10,
    highwayRoomCost: 2
  }
);

You can also provide a custom callback to provide weights or avoid certain rooms:

moveTo(
  creep,
  { pos: new RoomPosition(25, 25, 'W20N20'), range: 20 },
  {
    routeCallback: (room: string) => {
      if (hostileRooms.includes(room)) {
        return Infinity;
      }
      return undefined;
    }
  }
);

Dynamic Avoidance

You can specify an avoidTargets callback to re-route around hostile creeps:

moveTo(creep, storage.pos, {
  avoidTargets(room) {
    return Game.rooms[room]?.find(FIND_HOSTILE_CREEPS).map(creep => ({ pos: creep.pos, range: 3 })) ?? [];
  }
});

Overriding Default Config

You can import the config object and override default settings, if desired:

import { config } from 'screeps-cartographer';
config.DEFAULT_MOVE_OPTS.routeCallback = room => {
  if (Memory.rooms[room].isHostile) return Infinity;
  return;
};

Testing Cartographer

Cartographer includes a super-minimal Screeps bot which will maintain a spawn and generate scouts to collect room intelligence. This allows roads to be generated and visualized for debugging purposes, and also enables integration tests to catch regressions.

To run the tests, simply run the build and copy the contents of dist/test.js to Screeps. The tests will reset and run again automatically after a global reset. Test output is logged to the console.

Contributors