MOVEMENT · CROSS-ROOM ROUTE EXECUTION

Turn a Room Route into One Validated Exit Step

CURATED PATH

These links are maintained for a concrete prerequisite, failure mode, debugging step, or next lesson instead of generic topic similarity.

Movement

Recommended next steps

Check the room visibility boundaryVision prerequisiteDiagnose a route that still cannot be searchedFailure modeControl path costs inside each RoomNext path layer
Verification statusChinese source article: Reviewed in full · Official documentation: Checked against findRoute(), describeExits(), findClosestByPath(), and moveTo() documentation · Technical correction: Room planning, exit reachability, border transition, and destination behavior are separate stages

VERIFICATION

Evidence and test status

Chinese source article
Reviewed in full
Official documentation
Checked against findRoute(), describeExits(), findClosestByPath(), and moveTo() documentation
Technical correction
Room planning, exit reachability, border transition, and destination behavior are separate stages
JavaScript syntax
Passed by the editorial batch gate
Evidence level
Official documentation review, repository review, syntax checks, and static analysis only
Live multi-tick verification
Pending
Live cross-room route and border-transition test
Pending
Official engine source
Checked August 16, 2026 — Game.map.findRoute(), findExit(), describeExits(), and routeCallback cost handling
Callback normalization
Verified in source — Number(routeCallback(...)) || 1 makes falsy/non-numeric results fall back to cost 1; only Infinity is skipped
Same-room boundary
Verified — findRoute() returns [] for the same room while findExit() returns ERR_INVALID_ARGS when there is no first route step
Screeps Console test
Pending — no live routeCallback return-value transcript is claimed
Live cross-room movement test
Pending — no real-shard route traversal is claimed
Last editorial review
August 16, 2026

Use this guide when

Use this page when a Creep must travel to another room and you need to turn Game.map.findRoute() into one validated next-room step. The method plans rooms. It does not select every tile, move the Creep, prove the exit is reachable, or complete the task inside the destination room.

Use the ERR_NO_PATH guide when the immediate problem is a failed route or path search.

Keep four stages separate

StageQuestionEvidence
Room planWhich room should be entered next?findRoute() result
Exit selectionWhich exit tile is reachable now?Current-room path search
Border transitionDid the Creep enter the next room?Later-tick room name
Destination taskWhat position or object is the final target?Role-specific logic

A successful room route can still fail at the tile level. Reaching the target room can still leave the final task unfinished.

Use finite costs for preferences and Infinity for bans

function getRouteCost(roomName, policy) {
  if (policy.blockedRooms.has(roomName)) {
    return Infinity;
  }

  const intel = Memory.roomIntel?.[roomName];
  const recentRisk = Boolean(
    intel
    && Number.isInteger(intel.seenAt)
    && Game.time - intel.seenAt <= policy.maxIntelAge
    && intel.risk === 'high'
  );

  if (recentRisk) {
    return 10;
  }

  return policy.preferredRooms.has(roomName)
    ? 1
    : 2.5;
}

The values are relative policy weights, not travel ticks. A large finite cost keeps a room available as a last resort. Infinity removes it completely. Missing or stale intel is uncertainty, not proof of safety.

The current official engine normalizes every non-Infinity callback result with Number(value) || 1. That makes falsy or non-numeric values a subtle trap: 0, false, null, undefined, an empty string, and NaN all become room-entry cost 1. They are not zero-cost preferences and they do not block the room.

Validate routeCallback policy before the engine sees it

Do not overload callback falsiness with business meaning. Normalize your own configuration into either a positive finite cost or the explicit hard-block sentinel Infinity.

function normalizeRoutePolicyCost(value) {
  if (value === Infinity) {
    return Infinity;
  }

  const cost = Number(value);

  if (!Number.isFinite(cost) || cost <= 0) {
    return 1;
  }

  return cost;
}

function getConfiguredRouteCost(roomName) {
  if (Memory.routeAvoid?.includes(roomName)) {
    return Infinity;
  }

  return normalizeRoutePolicyCost(
    Memory.routeCosts?.[roomName]
  );
}

The helper's cost <= 0 → 1 rule is deliberate project validation. The engine expression itself does not repair negative finite values, so accepting only positive finite numbers or Infinity keeps configuration semantics explicit. Numeric strings such as "2.5" are coerced by the engine, but storing numeric policy as numbers is clearer.

Callback returnCurrent engine entry costRecommended interpretation
InfinityRoom skippedExplicit hard block
0, false, null, undefined, NaN1Do not use as block or zero-cost preference
"2.5"2.5Works through numeric coercion; prefer numeric configuration
positive finite numberthat numberNormal room-entry policy cost

Handle same-room completion and error codes first

function calculateRoomRoute(
  fromRoom,
  targetRoom,
  policy
) {
  if (
    typeof fromRoom !== 'string'
    || typeof targetRoom !== 'string'
  ) {
    return {
      status: 'room-name-invalid',
      steps: []
    };
  }

  if (fromRoom === targetRoom) {
    return {
      status: 'target-room-reached',
      steps: []
    };
  }

  const route = Game.map.findRoute(
    fromRoom,
    targetRoom,
    {
      routeCallback(roomName) {
        return getRouteCost(roomName, policy);
      }
    }
  );

  if (!Array.isArray(route)) {
    return {
      status: 'route-failed',
      code: route,
      steps: []
    };
  }

  return {
    status: 'route-ready',
    code: OK,
    steps: route.map(step => ({
      exit: step.exit,
      room: step.room
    }))
  };
}

Do not read route[0] until the result is confirmed to be an array. An empty array is correct when the current room already equals the target room. That boundary is specific to findRoute(). Game.map.findExit(), which calls findRoute() internally, returns ERR_INVALID_ARGS when there is no first route step; invalid room names return ERR_NO_PATH from findRoute().

Validate only the first step from the current room

function validateFirstRouteStep(
  currentRoom,
  routeResult
) {
  if (routeResult.status !== 'route-ready') {
    return routeResult;
  }

  const step = routeResult.steps[0];

  if (!step) {
    return {
      status: 'first-step-missing'
    };
  }

  const exits = Game.map.describeExits(currentRoom);

  if (!exits || exits[step.exit] !== step.room) {
    return {
      status: 'first-step-invalid',
      step
    };
  }

  return {
    status: 'first-step-ready',
    step
  };
}

The route was built from the current room, so the first step is the only room-level instruction needed now. Searching an old full route for a matching exit can select a stale step after an unexpected room transition.

Choose a reachable exit tile in the current room

function chooseExitPosition(creep, step) {
  if (!creep || !step) {
    return {
      status: 'input-missing',
      position: null
    };
  }

  const position = creep.pos.findClosestByPath(
    step.exit,
    {
      maxOps: 4000,
      ignoreCreeps: false
    }
  );

  return position
    ? { status: 'exit-position-ready', position }
    : { status: 'exit-position-unreachable', position: null };
}

findClosestByPath() answers a tile-level question inside the current room. A valid room step does not guarantee that walls, structures, traffic, or custom costs allow access to an exit tile.

Execute one room step and rebuild after transition

function moveTowardTargetRoom(
  creep,
  targetRoom,
  policy
) {
  if (!creep) {
    return { status: 'creep-missing' };
  }

  if (creep.room.name === targetRoom) {
    delete creep.memory.routeStep;
    return { status: 'target-room-reached' };
  }

  const routeResult = calculateRoomRoute(
    creep.room.name,
    targetRoom,
    policy
  );
  const firstStep = validateFirstRouteStep(
    creep.room.name,
    routeResult
  );

  if (firstStep.status !== 'first-step-ready') {
    return firstStep;
  }

  const exitResult = chooseExitPosition(
    creep,
    firstStep.step
  );

  if (exitResult.status !== 'exit-position-ready') {
    return exitResult;
  }

  creep.memory.routeStep = {
    fromRoom: creep.room.name,
    targetRoom,
    nextRoom: firstStep.step.room,
    exit: firstStep.step.exit,
    builtAt: Game.time
  };

  const moveResult = creep.moveTo(
    exitResult.position,
    {
      range: 0,
      reusePath: 5,
      maxRooms: 1
    }
  );

  return {
    status: moveResult === OK
      ? 'movement-submitted'
      : 'movement-rejected',
    moveResult,
    nextRoom: firstStep.step.room,
    exitPosition: {
      roomName: exitResult.position.roomName,
      x: exitResult.position.x,
      y: exitResult.position.y
    }
  };
}

Recalculate after creep.room.name changes. This trades some route calculations for a small, recoverable state boundary. A shared route cache can be added later only with destination, policy-version, and invalidation rules.

Treat the room edge as a later-tick observation

moveTo() submits movement for the current tick. Record the current room, next room, exit direction, exit position, and movement result. On later ticks, compare the new roomName:x:y. An accepted order does not prove the Creep crossed the border during the same script execution.

Room-name patterns are not live safety evidence

Highway or sector classification can influence preference, but it does not reveal current hostiles, Towers, ownership, Invader Cores, Nukes, or temporary combat conditions. Timestamp any stored intel and expose an unknown state rather than silently treating missing vision as safe.

Entering the target room is not final completion

After the room transition, the role still needs a target position, Flag, Controller, Source, Structure, or object ID. Use the RoomPosition guide for local range choices and the CostMatrix guide for custom tile policy.

Verify route, exit, and transition separately

Test same-room completion, a valid adjacent step, a hard-blocked corridor, a finite-risk corridor, an unreachable exit tile, and a successful room transition as separate cases. This revision contains static analysis and syntax checks only; live cross-room movement remains pending.

Official documentation

SOURCE AND SCOPE

Review the source, evidence, or next system

This English guide keeps the technical scope and verification boundaries of its Chinese source. The verification record shows which checks were completed and which runtime observations are still pending.