Implemented basic harvester

Finished a basic harvester logic which sets its harvesting and destination (where it brings the resource to) in memory which can be set by the overlord.
The harvester uses a state machine which persists also via memory to know what its doing and currently just travels to its harvest_spot, harvest, travels to its destination and delivers there and then repeats.

This means there is currently close to no error handling. For example if the storage is full, the harvest target is cooling down or the creep dies of age or damage is an issue. The creep also doesn't register its death or damage in anyway.
main
Peery 2 years ago
parent 167f20d8b3
commit 9e5c269238

@ -1,4 +1,10 @@
const { sum } = require("lodash");
const { sum, isNil } = require("lodash");
const STATE_INIT = 0;
const STATE_GOING_TO_SOURCE = 1;
const STATE_HARVESTING = 2;
const STATE_GOING_TO_DESTINATION = 3;
const STATE_DELIVERING = 4;
var roleHarvester = {
@ -15,20 +21,45 @@ var roleHarvester = {
* @return {string} name of the new creep
*/
spawn: function(spawn, postfix, owner_id) {
if (spawn.spawnCreep(this.recommended_parts, "harvester-"+postfix, {dryRun: true}) === OK) {
console.log("roleHarvester: Spawn "+spawn.name+" is creating harvester-"+postfix+" with recommended parts ...");
spawn.spawnCreep(this.recommended_parts, "harvester-"+postfix, {memory: {"owner": owner_id, "role": "Harv"}});
let creep_name = "harvester-"+postfix;
if (spawn.spawnCreep(this.recommended_parts, creep_name, {dryRun: true}) === OK) {
console.log("roleHarvester: Spawn "+spawn.name+" is creating "+creep_name+" with recommended parts ...");
spawn.spawnCreep(this.recommended_parts, creep_name, {memory: {"owner": owner_id, "role": "Harv"}});
}
else if (spawn.spawnCreep(this.minimum_parts, "harvester-"+postfix, {dryRun: true}) === OK) {
console.log("roleHarvester: Spawn "+spawn.name+" is creating harvester-"+postfix+" with minimum parts ...");
spawn.spawnCreep(this.minimum_parts, "harvester-"+postfix, {memory: {"owner": owner_id, "role": "Harv"}});
else if (spawn.spawnCreep(this.minimum_parts, creep_name, {dryRun: true}) === OK) {
console.log("roleHarvester: Spawn "+spawn.name+" is creating "+creep_name+" with minimum parts ...");
spawn.spawnCreep(this.minimum_parts, creep_name, {memory: {"owner": owner_id, "role": "Harv"}});
}
else {
console.log("roleHarvester: Spawn \""+spawn.name+"\" couldn't afford a harvester! D: Result: ", spawn.spawnCreep(this.minimum_parts, "harvester-"+postfix, {dryRun: true}));
console.log("roleHarvester: Spawn \""+spawn.name+"\" couldn't afford a harvester! D: Result: ", spawn.spawnCreep(this.minimum_parts, creep_name, {dryRun: true}));
throw "Not enough energy!";
}
Memory.creeps[creep_name].harvest_spot = {
id: null,
resource_type: null,
structure_pos: null,
goto_pos: null,
};
Memory.creeps[creep_name].destination_spot = {
id: null,
structure_pos: null,
goto_pos: null,
};
return "harvester-"+postfix;
return creep_name;
},
/**
* Checks if the creep has all the parts necessary to work its role
* @param {Creep} creep
* @return bool that indicates if the creep can fullfill its role
*/
can_work: function(creep) {
if (creep.getActiveBodyparts(MOVE) >= 1 && creep.getActiveBodyparts(WORK) >= 1 && creep.getActiveBodyparts(CARRY) >= 1) {
return true;
}
return false;
},
/**
@ -36,23 +67,183 @@ var roleHarvester = {
* @param {Creep} creep
*/
run: function(creep) {
if (creep.id === undefined) { // id is undefined if the creep was created this tick
if (creep.id === undefined) { // id is undefined if the creep was created this tick, should happen only once
this.switch_state(creep, STATE_INIT);
return
}
console.log("roleHarvester ["+creep.name+"]: run() " + creep + " id:", creep.id);
// TODO implement state machine here about what the harvester is up to. Currently it only moves to the nearest source.
switch (creep.memory.state) {
case STATE_INIT:
this.init_creep(creep);
break;
case STATE_GOING_TO_SOURCE:
if (!creep.spawning) {
this.go_to_source(creep);
}
break;
case STATE_HARVESTING:
this.harvest(creep);
break;
case STATE_GOING_TO_DESTINATION:
this.go_to_destination(creep);
break;
case STATE_DELIVERING:
this.delivering(creep);
break;
default:
console.log("roleHarvester ["+creep.name+"]: run() unknown state!");
//this.switch_state(creep, STATE_INIT);
};
},
let goals = _.map(creep.room.find(FIND_SOURCES), function(source) {
return {pos: source.pos, range: 1};
});
/**
* Switches the state of the creep by editing the memory
* @param {Creep} creep
* @param {int} state State constant as given (e.g. STATE_HARVESTING)
*/
switch_state: function(creep, state) {
console.log("roleHarvester ["+creep.name+"]: Changing state to "+state);
console.log(creep, creep.memory);
creep.memory.state = state
},
/**
* Inits creep and sets the assigned source to be the nearest source.
* @param {*} creep
*/
init_creep: function(creep) { // TODO make source assignment aware of other harvesters to spread them
if (creep.memory.harvest_spot.id !== null) {
// harvest spot has already been given (by overlord?). Don't have to decide on my own!
}
else {
let room_sources = creep.room.find(FIND_SOURCES);
let goals = _.map(room_sources, function(source) {
return {pos: source.pos, range: 1};
});
let nearest_source = PathFinder.search(creep.pos, goals);
//console.log("roleHarvester ["+creep.name+"]: init_creep() remembering target position: " + nearest_source.path[nearest_source.path.length-1])
for (let source of room_sources) {
if (source.pos.isNearTo(nearest_source.path[nearest_source.path.length-1])) {
creep.memory.harvest_spot.id = source.id;
creep.memory.harvest_spot.structure_pos = source.pos;
creep.memory.harvest_spot.goto_pos = nearest_source.path[nearest_source.path.length-1];
creep.memory.harvest_spot.resource_type = RESOURCE_ENERGY;
break;
}
}
}
if (creep.memory.destination_spot.structure_pos !== null && creep.memory.destination_spot.goto_pos !== null) {
// destination spot has already been given (by the overlord?). Don't have to decide on my own!
}
else if (creep.memory.destination_spot.structure_pos !== null) { // goto_pos === null and we have to find it on our own later
//
}
else {
console.log("roleHarvester ["+creep.name+"] init_creep(): Don't have a destination_spot and autonomously finding one is not implemented!");
throw "No Destination spot";
}
let nearest_source = PathFinder.search(creep.pos, goals);
console.log("roleHarvester ["+creep.name+"]: run() moving creept towards " + nearest_source.path[nearest_source.path.length-1], nearest_source);
console.log("roleHarvester ["+creep.name+"]: run() moving to direction " + nearest_source.path[0]);
creep.moveTo(nearest_source.path[0]);
console.log("roleHarvester ["+creep.name+"] init_creep(): harvest task has been setup and committed to memory!")
this.switch_state(creep, STATE_GOING_TO_SOURCE);
},
/**
* Moves the creep to its assigned source. The assigned source is a roomPosition in creep.memory.harvest_target_pos
* @param {Creep} creep
*/
go_to_source: function(creep) {
if (creep.memory.harvest_spot.goto_pos === null) { // find goto pos, if needed
let structure_pos = new RoomPosition(creep.memory.harvest_spot.structure_pos.x, creep.memory.harvest_spot.structure_pos.y, creep.memory.hravest_spot.structure_pos.roomName);
let result = PathFinder.search(creep.pos, {pos: structure_pos, range: 1});
creep.memory.harvest_spot.goto_pos = result.path[result.path.length-1];
}
let target_pos = new RoomPosition(creep.memory.harvest_spot.goto_pos.x, creep.memory.harvest_spot.goto_pos.y, creep.memory.harvest_spot.goto_pos.roomName);
if (creep.pos.isEqualTo(target_pos)) {
console.log("roleHarvester ["+creep.name+"]: go_to_source() arrived at target!");
this.switch_state(creep, STATE_HARVESTING);
}
//console.log("roleHarvester ["+creep.name+"]: go_to_source() moving creep towards " + target_pos);
creep.moveTo(target_pos, {visualizePathStyle: {stroke: '#ffaa00'}});
},
/**
* Lets the creep harvest its assigned source
* @param {Creep} creep
*/
harvest: function(creep) {
if (creep.store.getFreeCapacity(creep.memory.harvest_spot.resource_type) > 0) {
let target = Game.getObjectById(creep.memory.harvest_spot.id);
let err = creep.harvest(target);
if (err != OK) {
console.log("roleHarvester [ERROR]["+creep.name+"] harvest(): Failed to harvest at target "+target.pos+" with error: "+err);
if (err != ERR_NOT_IN_RANGE) {
console.log("roleHarvester ["+creep.name+"] harvest(): Failed to harvest because of wrong goto_pos trying to recalculate and switch back to moving ...");
creep.memory.harvest_spot.structure_pos = target.pos;
let result = PathFinder.search(creep.pos, {pos: target.pos, range: 1});
creep.memory.harvest_spot.goto_pos = result.path[result.path.length-1];
this.switch_state(creep, STATE_GOING_TO_SOURCE);
}
}
}
else {
this.switch_state(creep, STATE_GOING_TO_DESTINATION);
}
},
/**
* Goes to the delivery destination of the creep as dictated by destination_spot in its memory
* @param {Creep} creep
*/
go_to_destination: function(creep) {
if (creep.memory.destination_spot.goto_pos === null) { // find goto pos, if needed
let structure_pos = new RoomPosition(creep.memory.destination_spot.structure_pos.x, creep.memory.destination_spot.structure_pos.y, creep.memory.destination_spot.structure_pos.roomName);
let result = PathFinder.search(creep.pos, {pos: structure_pos, range: 1});
creep.memory.destination_spot.goto_pos = result.path[result.path.length-1];
}
let target_pos = new RoomPosition(creep.memory.destination_spot.goto_pos.x, creep.memory.destination_spot.goto_pos.y, creep.memory.destination_spot.goto_pos.roomName);
if (creep.pos.isEqualTo(target_pos)) {
console.log("roleHarvester ["+creep.name+"]: go_to_destination() arrived at target!");
this.switch_state(creep, STATE_DELIVERING);
}
creep.moveTo(target_pos, {visualizePathStyle: {stroke: '#ffaa00'}});
},
/**
* Lets the creep store its resources into the destination defined by the memory destination_spot
* @param {Creep} creep
*/
delivering: function(creep) {
let had_issue = false;
let storage = Game.getObjectById(creep.memory.destination_spot.id);
for (const resource_type in creep.store) {
console.log("roleHarvester ["+creep.name+"] delivering(): "+resource_type);
let err = creep.transfer(storage, resource_type);
if (err != OK) {
had_issue = true;
console.log("roleHarvester [ERROR]["+creep.name+"] delivering(): Failed to transfer resources with error: "+err);
creep.say("ERROR!");
}
}
if (!had_issue) {
this.switch_state(creep, STATE_GOING_TO_SOURCE);
}
else {
// TODO what to do if delivery failed?
}
},
};

@ -4,14 +4,14 @@ var harvester = require('creeps_harvester');
/**
* Overlord that sets up a room given only a single spawn with a minimum energy to build one harvester.
*
* Setup is considered finished once 3 spawns have been built and at least one creep is maintaining the RCL
* Setup is considered finished once 3 spawns have been built and at least one creep is maintaining the RCL and at least one harvester is working
*/
/** Checks the room and sets out the tasks to achieve the goal */
setup_overlord.prototype.run = function () { // TODO somehow these aren't changing anything
setup_overlord.prototype.run = function () {
console.log("SetupOverlord: Running \""+this.id+"\"! :) Chilling for now ...");
console.log("SetupOverlord: has the following underlings: ", this.underlings['spawns'], this.underlings['creeps']);
console.log("SetupOverlord: \""+this.id+"\"has the following underlings: ", this.underlings['spawns'], this.underlings['creeps']);
if (this.underlings['creeps'].length === 0 && this.underlings['spawns'].length === 0) { // got NOTHING to work with
console.log("SetupOverlord: ERROR "+this.id+" has nothing! ;W;");
@ -24,7 +24,8 @@ setup_overlord.prototype.run = function () { // TODO somehow these aren't chang
if (spawn.store[RESOURCE_ENERGY] > harvester.min_cost) { // can afford a harvester
console.log("SetupOverlord: Spawn "+spawn.name+" can afford a harvester! ("+harvester.min_cost+") \\o/");
let new_harvester_name = harvester.spawn(spawn, this.id+"-"+this.underlings['creeps'].length, this.id);
this.add_creep(new_harvester_name); // TODO replace with ID? creep.id is only defined next tick though ...
this.set_creep_destination_spot(new_harvester_name, spawn);
this.add_creep(new_harvester_name);
break;
}
}
@ -32,4 +33,32 @@ setup_overlord.prototype.run = function () { // TODO somehow these aren't chang
}
};
/**
* Set a creeps destination spot in their memory. This defines where a creep delivers their stuff to.
* The goto_pos is set to null to indicate to the creep to find their standing spot on their own.
*
* @param {string} creep name of the creep to which this deliver destination will be given
* @param {RoomPosition} target must be a game object with an id and a pos property
*/
setup_overlord.prototype.set_creep_destination_spot = function(creep, target) {
console.log("SetupOverlord ["+this.id+"]: Instructing creep \""+creep+"\" to set their destination to "+target.pos);
Memory.creeps[creep].destination_spot.id = target.id;
Memory.creeps[creep].destination_spot.structure_pos = target.pos;
Memory.creeps[creep].destination_spot.goto_pos = null;
};
/**
* Set a creeps destination spot in their memory. This defines where a creep delivers their stuff to.
* The goto_pos is set to null to indicate to the creep to find their standing spot on their own.
*
* @param {string} creep name of the creep to which this deliver destination will be given
* @param {RoomPosition} target must be a game object with an id and a pos property
*/
setup_overlord.prototype.set_creep_harvest_spot = function(creep, target) {
console.log("SetupOverlord ["+this.id+"]: Instructing creep \""+creep+"\" to set their destination to "+target.pos);
Memory.creeps[creep].harvest_spot.id = target.id;
Memory.creeps[creep].harvest_spot.structure_pos = target.pos;
Memory.creeps[creep].harvest_spot.goto_pos = null;
};
module.exports = setup_overlord;
Loading…
Cancel
Save