Quick answer
Keep Builder1's harvesting state, then give its work phase one clear priority: build the first owned Construction Site, otherwise repair the first selected damaged Structure, otherwise upgrade the Controller. Use return after each selected task so one tick does not continue into lower-priority branches.
The beginner task priority
- Build: an owned Construction Site exists.
- Repair: no site exists, but a selected Structure has
hits < hitsMax. - Upgrade: neither of the first two targets exists.
Priority does not make the Builder universally efficient. It only ensures that one main work action is selected for the current tick.
Which Structures are included in repair?
This lesson repairs:
- structures that report
my === true; - Roads;
- Containers.
It excludes Walls and Ramparts because defensive hit targets need a separate policy. Otherwise one Builder can spend nearly all available Energy on very high hit capacities.
Read-only precheck
State impact: Read-only.
const creep = Game.creeps['Builder1'];
if (!creep) {
console.log('Builder1 was not found.');
} else {
const sites =
creep.room.find(FIND_MY_CONSTRUCTION_SITES);
const damaged =
creep.room.find(FIND_STRUCTURES, {
filter: function (structure) {
const repairable =
structure.my === true ||
structure.structureType === STRUCTURE_ROAD ||
structure.structureType === STRUCTURE_CONTAINER;
const excluded =
structure.structureType === STRUCTURE_WALL ||
structure.structureType === STRUCTURE_RAMPART;
return repairable &&
!excluded &&
structure.hits < structure.hitsMax;
}
});
console.log(JSON.stringify({
siteCount: sites.length,
damagedCount: damaged.length,
controllerFound: Boolean(creep.room.controller),
carriedEnergy:
creep.store.getUsedCapacity(RESOURCE_ENERGY),
working: creep.memory.working === true
}));
}
Complete Builder1 priority code
State impact: Calls harvest, build, repair, upgrade, and movement methods, and writes creep.memory.working.
const CREEP_NAME = 'Builder1';
function moveToTarget(creep, target, label) {
const moveResult = creep.moveTo(target, {
reusePath: 5
});
if (moveResult !== OK &&
moveResult !== ERR_TIRED) {
console.log(
creep.name +
' moveTo(' +
label +
') returned ' +
moveResult +
'.'
);
}
}
module.exports.loop = function () {
const creep = Game.creeps[CREEP_NAME];
if (!creep) {
console.log(
CREEP_NAME +
' was not found. Create it or check the name.'
);
return;
}
if (creep.spawning) {
return;
}
const source = creep.room.find(FIND_SOURCES)[0];
if (!source) {
console.log(
CREEP_NAME +
' cannot find a visible Source.'
);
return;
}
const usedEnergy =
creep.store.getUsedCapacity(RESOURCE_ENERGY) || 0;
const freeEnergy =
creep.store.getFreeCapacity(RESOURCE_ENERGY) || 0;
if (usedEnergy === 0) {
creep.memory.working = false;
}
if (freeEnergy === 0) {
creep.memory.working = true;
}
if (!creep.memory.working) {
const harvestResult = creep.harvest(source);
if (harvestResult === ERR_NOT_IN_RANGE) {
moveToTarget(creep, source, 'Source');
} else if (harvestResult !== OK &&
harvestResult !== ERR_NOT_ENOUGH_RESOURCES) {
console.log(
CREEP_NAME +
' harvest() returned ' +
harvestResult +
'.'
);
}
return;
}
const site =
creep.room.find(FIND_MY_CONSTRUCTION_SITES)[0];
if (site) {
const buildResult = creep.build(site);
if (buildResult === ERR_NOT_IN_RANGE) {
moveToTarget(creep, site, 'Construction Site');
} else if (buildResult !== OK) {
console.log(
CREEP_NAME +
' build() returned ' +
buildResult +
'.'
);
}
return;
}
const damaged =
creep.room.find(FIND_STRUCTURES, {
filter: function (structure) {
const repairable =
structure.my === true ||
structure.structureType === STRUCTURE_ROAD ||
structure.structureType === STRUCTURE_CONTAINER;
const excluded =
structure.structureType === STRUCTURE_WALL ||
structure.structureType === STRUCTURE_RAMPART;
return repairable &&
!excluded &&
structure.hits < structure.hitsMax;
}
})[0];
if (damaged) {
const repairResult = creep.repair(damaged);
if (repairResult === ERR_NOT_IN_RANGE) {
moveToTarget(creep, damaged, 'damaged Structure');
} else if (repairResult !== OK) {
console.log(
CREEP_NAME +
' repair() returned ' +
repairResult +
'.'
);
}
return;
}
const controller = creep.room.controller;
if (!controller) {
console.log(
CREEP_NAME +
' cannot find a Controller.'
);
return;
}
const upgradeResult =
creep.upgradeController(controller);
if (upgradeResult === ERR_NOT_IN_RANGE) {
moveToTarget(creep, controller, 'Controller');
} else if (upgradeResult !== OK) {
console.log(
CREEP_NAME +
' upgradeController() returned ' +
upgradeResult +
'.'
);
}
};
Why each task branch returns
| Target found | Action | Continue to lower priority? |
|---|---|---|
| Construction Site | build() | No |
| Selected damaged Structure | repair() | No |
| Neither | upgradeController() | Final branch |
A return means the current tick already has its chosen task. It prevents the same function from continuing into another branch.
Action results to inspect
| Result | Typical meaning | Response |
|---|---|---|
ERR_NOT_IN_RANGE | Target is farther than the action range. | Move toward it. |
ERR_NOT_ENOUGH_RESOURCES | The Creep has no carried Energy. | Switch back to harvesting. |
ERR_NO_BODYPART | No active WORK remains. | Inspect damage. |
ERR_INVALID_TARGET | The selected target is no longer valid. | Find targets again next tick. |
OK | The action was scheduled. | Observe later ticks. |
build(), repair(), and upgradeController() all have range 3 under the current API.
What to observe
- Builder1 harvests when empty.
- It builds while any owned site exists.
- When no site exists, it selects an allowed damaged Structure.
- When neither target exists, it moves to the Controller.
- Energy decreases during work.
- The Creep returns to the Source when empty.
- Only one main task branch runs in the current tick.
Common problems
Walls and Ramparts are never repaired
They are intentionally excluded. Add a separate defensive hit policy later.
The Builder does not choose the nearest target
The code selects the first search result so the article can focus on task priority.
The Creep stands near a target
Inspect the saved action result, carried Energy, active WORK, and target validity.
It upgrades even though you expected repair
Check whether the target passes the ownership, type, exclusion, and hit conditions.
Debugging checklist
- Confirm Builder1 exists and is not spawning.
- Confirm a visible Source exists.
- Inspect
memory.workingand Store values. - Check for owned Construction Sites.
- Check the repair filter one condition at a time.
- Confirm Walls and Ramparts are intentionally excluded.
- Confirm the Controller exists before the fallback action.
- Save every action and movement result.
- Verify changed progress or hits on later ticks.
Known limits
- No nearest-target sorting.
- No different thresholds for Roads and Containers.
- No Wall or Rampart hit policy.
- No multiple-Builder assignment.
- No target caching or CPU optimization.
- No automatic Construction Site planning.
Scope and next step
This is a one-Builder learning priority, not a complete maintenance system. Continue with the first room code lesson to combine spawning, harvesting, delivery, upgrading, building, and repair.
Frequently asked questions
Why are Walls and Ramparts excluded?
They need separate target-hit policies.
Why choose only one target?
The lesson explains priority before sorting or assignment.
How close must the Creep be?
Build and repair both work within range 3.
Why upgrade when idle?
It is the final fallback after build and repair targets are absent.