What you will complete in this lesson
This lesson has one practical goal: after you save the code, one existing Creep should move toward a visible Source and begin collecting Energy.
You will use the tick model from the previous lesson: find the current Creep, choose one action, let the game advance, and read the new state on a later tick.
Lesson boundary: the Creep stops when its Store is full. Returning Energy to a Spawn is the next lesson.
What your Creep needs
Use one existing Creep with a real name from your account. The basic worker pattern in this lesson uses three body parts:
| Body part | Purpose here |
|---|---|
WORK | Required for harvest(). |
MOVE | Lets the Creep travel toward the Source. |
CARRY | Provides Store capacity so the harvested Energy can be retained and delivered later. |
The API requires WORK for harvesting. A specialized Creep can harvest without free carrying capacity, but the resource may be dropped on the ground instead of entering its Store. This first worker lesson deliberately uses CARRY.
The WORK, CARRY, and MOVE guide explains these abilities in more detail.
Copy the real Creep name
State impact: read-only. Run this in the Console:
Object.keys(Game.creeps);
Copy one exact name from the returned array. Names are case-sensitive. The examples below use Harvester1, but that value must be replaced when your Creep has another name.
You can also inspect the selected Creep before changing the main loop:
const creep = Game.creeps['Harvester1'];
console.log(JSON.stringify({
found: Boolean(creep),
spawning: creep ? creep.spawning : null,
activeWork: creep ? creep.getActiveBodyparts(WORK) : null,
activeMove: creep ? creep.getActiveBodyparts(MOVE) : null,
freeEnergyCapacity: creep
? creep.store.getFreeCapacity(RESOURCE_ENERGY)
: null
}));
Continue after confirming that the Creep exists, has finished spawning, and has at least one active WORK part. It also needs an active MOVE part when it is not already beside a Source.
Start with the minimal working code
State impact: this script may move one Creep and harvest a Source. Replace the example name before saving it.
const CREEP_NAME = 'Harvester1';
module.exports.loop = function () {
const creep = Game.creeps[CREEP_NAME];
if (!creep || creep.spawning) return;
const source = creep.room.find(FIND_SOURCES)[0];
if (!source) return;
const harvestResult = creep.harvest(source);
if (harvestResult === ERR_NOT_IN_RANGE) {
creep.moveTo(source);
}
};
The script follows one rule:
try to harvest → move only when the Source is out of range → try again on the next tick.
Room.find(FIND_SOURCES) returns an array. This first lesson uses the first Source in that array; it does not claim that the Source is the nearest or most efficient choice.
Use the guarded version while learning
The minimal version is easy to read, but it hides useful evidence. The following version keeps the same behavior while checking the Creep, body parts, Source, action result, and movement result.
State impact: it may move and harvest with one named Creep. The temporary status log runs once every five ticks and should be removed after verification.
const CREEP_NAME = 'Harvester1';
const DEBUG_HARVEST = true;
module.exports.loop = function () {
const creep = Game.creeps[CREEP_NAME];
if (!creep) {
console.log(
CREEP_NAME +
' was not found. Check the name and capitalization.'
);
return;
}
if (creep.spawning) return;
if (creep.getActiveBodyparts(WORK) === 0) {
console.log(CREEP_NAME + ' has no active WORK part.');
return;
}
const source = creep.room.find(FIND_SOURCES)[0];
if (!source) {
console.log(
'No visible Source was found in ' +
creep.room.name +
'.'
);
return;
}
const harvestResult = creep.harvest(source);
let moveResult = null;
if (harvestResult === ERR_NOT_IN_RANGE) {
if (creep.getActiveBodyparts(MOVE) === 0) {
console.log(CREEP_NAME + ' has no active MOVE part.');
return;
}
moveResult = creep.moveTo(source, { reusePath: 5 });
}
if (DEBUG_HARVEST && Game.time % 5 === 0) {
console.log(JSON.stringify({
tick: Game.time,
creep: CREEP_NAME,
range: creep.pos.getRangeTo(source),
harvestResult: harvestResult,
moveResult: moveResult,
energy: creep.store.getUsedCapacity(RESOURCE_ENERGY),
capacity: creep.store.getCapacity(RESOURCE_ENERGY)
}));
}
if (
harvestResult !== OK &&
harvestResult !== ERR_NOT_IN_RANGE &&
harvestResult !== ERR_FULL &&
harvestResult !== ERR_NOT_ENOUGH_RESOURCES
) {
console.log(
CREEP_NAME +
' harvest() returned ' +
harvestResult +
'.'
);
}
if (
moveResult !== null &&
moveResult !== OK &&
moveResult !== ERR_TIRED
) {
console.log(
CREEP_NAME +
' moveTo(Source) returned ' +
moveResult +
'.'
);
}
};
The script deliberately calls harvest() before movement. That preserves the real reason for the decision: movement happens only when the work action reports ERR_NOT_IN_RANGE.
What you should see across later ticks
The exact tick numbers, route, and Energy values depend on your room. The expected state sequence is:
| Observed state | harvestResult | moveResult | Meaning |
|---|---|---|---|
| The Creep is several squares away. | ERR_NOT_IN_RANGE | Usually OK | Movement was scheduled for this tick. |
| The Creep is approaching the Source. | ERR_NOT_IN_RANGE | OK or a temporary movement result | The loop will try again after the game advances. |
| The Creep is adjacent to the Source. | OK | null | Harvesting was scheduled; the same-tick Store reading may still show the previous state. |
| A later tick begins. | OK while capacity remains | null | The Creep's stored Energy should be higher than before. |
| The worker Store is full. | ERR_FULL | null | This lesson is complete and the delivery step is now required. |
OK means the command was scheduled successfully. Verify the result by reading position and Store values on later ticks rather than expecting the same script execution to contain the future state.
Four results to understand first
| Return code | Beginner meaning | Response in this lesson |
|---|---|---|
OK | The harvest command was scheduled. | Inspect a later tick. |
ERR_NOT_IN_RANGE | The Creep is not adjacent to the Source. | Call moveTo(source). |
ERR_NOT_ENOUGH_RESOURCES | The Source currently has no harvestable Energy. | Wait nearby for regeneration. |
ERR_FULL | The worker cannot receive more Energy. | Continue to the delivery lesson. |
When another result appears, preserve the number and use the English return-code reference. For example, ERR_NO_BODYPART means that no active WORK part is available for harvesting.
Fix the three most common problems
The script says the Creep was not found
Run Object.keys(Game.creeps) again and copy the exact name. Do not assume that the tutorial name exists in your account.
The Creep reaches the Source but Energy does not increase
Inspect harvestResult, creep.getActiveBodyparts(WORK), and the Source's current Energy. A missing active WORK part and an empty Source are different conditions.
The Creep does not approach the Source
Inspect moveResult and confirm that the Creep has an active MOVE part. Movement, fatigue, traffic, and pathfinding problems belong to the Creep movement debugging guide, not to the harvesting action itself.
Completion check
You have completed this lesson when you can do all of the following:
- replace
Harvester1with a real Creep name; - explain why the script checks the Creep and Source before using them;
- explain why
harvest()is called beforemoveTo(); - observe
ERR_NOT_IN_RANGEwhile the Creep approaches; - observe
OKwhen harvesting can be scheduled; - confirm that stored Energy increases on later ticks;
- recognize a full Store as the boundary of this lesson.
Continue to Energy delivery
Your Creep can now collect Energy, but the current script gives it no destination after the Store becomes full.
Make the Creep deliver Energy to a Spawn →
Return to the English beginner roadmap to review the complete twelve-lesson sequence.