Skip to content

Instantly share code, notes, and snippets.

@glsorre
Last active January 26, 2024 21:07
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save glsorre/fd6aae59f4193c63c8c3f28b0aeb51e2 to your computer and use it in GitHub Desktop.
Save glsorre/fd6aae59f4193c63c8c3f28b0aeb51e2 to your computer and use it in GitHub Desktop.
A functional boilerplate for https://screeps.com with creeps based on the fsm pattern

A functional boilerplate for screeps.com with creeps based on the fsm pattern

A simple boilerplate for screeps.com. It easy to configure and expand.

The creeps of 3 types (harvesters, builders and upgraders) are based on the finite-state-machine pattern. They are easily extensible.

The project is made of 9 files:

  • main.js: the main loop file containing the conf of creeps in the creeps_conf array;
  • extended.room.js: a file extending the base room object of the game with useful resources;
  • spawner.js: a file that manages the creep spawning process;
  • builders.js, harvesters.js and upgraders.js: helpers files to run the different creep types;
  • extended.builder.js, exented.harvester.js and extended.upgrader.js: files extending the base creep object of the game with a finite state machine model.

Configuration

creeps_conf = [
        {   
            name_suffix: 'harvester', // name suffix of the creeps
            tot: 5, // total number of creeps of this type
            bodies: [WORK, CARRY, MOVE], // bodies to spawn
            manager: harvesters // the name of the manager
        },
        ... // ADD CREEP TYPE AT REQUEST
    ];

Interfaces

Creep Manager

var creeps = {
    get: function (creeps, name) {
        // function that returns all the creep of one type
    },
    manage: function (creeps, room) {
        // function to manage the creeps
    },
    run: function (creep, room, current_status) {
        // function to run the single creeps
    }
}

Creep Finite-State-Machine

var extended_creep = {
    states: {
        harvest: { // name of he state
            code: 0, // state code
            run: function (room, status) {
                // function to execute the state
            },
            transition: function (room, status) {
                // function to manage the transition to another state
            }
        },
        build: {
            code: 1,
            run: function (room) {

            },
            transition: function (room) {

            }
        },
        ... // ADD STATES AT REQUEST
    }
}
var extended_creep = require('extended.builder');
var creeps = {
get: function (creeps, name) {
let filtered_creeps = {}
for(let creep_name in creeps) {
if (creep_name.startsWith(name)) {
filtered_creeps[creep_name] = _.extend(creeps[creep_name], extended_creep);
}
}
return filtered_creeps
},
manage: function (creeps, room) {
let current_status = {};
current_status.tot = 0;
for (let [key, state] of Object.entries(extended_creep.states)) {
let tot = _.filter(creeps, (creep) => creep.memory.state == state.code).length;
current_status[key] = tot;
console.log('module builders - ' + key + 'ing' + ': ' + tot);
}
for (let [key, number] of Object.entries(current_status)) {
current_status.tot += number;
}
for (let [key, creep] of Object.entries(creeps)) {
this.run(creep, room, current_status)
}
},
run: function (creep, room, current_status) {
if (!creep.memory.state) {
creep.memory.state = creep.states.harvest.code;
}
for (let key in creep.states) {
if (creep.memory.state == creep.states[key].code) {
creep.states[key].run.call(creep, room, current_status);
creep.states[key].transition.call(creep, room, current_status);
break;
}
}
}
}
module.exports = creeps;
var extended_creep = {
states: {
harvest: {
code: 0,
run: function (room, status) {
if (this.harvest(room.sources[0]) == ERR_NOT_IN_RANGE) {
this.moveTo(room.sources[0], { visualizePathStyle: { stroke: '#ffffff' } });
}
},
transition: function (room, status) {
if (this.carry.energy == this.carryCapacity) {
this.memory.state = this.states.build.code;
this.say('build');
}
}
},
build: {
code: 1,
run: function (room) {
if (this.build(room.buildables[0], RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
this.moveTo(room.buildables[0], { visualizePathStyle: { stroke: '#ffffff' } });
}
},
transition: function (room) {
if (this.carry.energy == 0 || room.buildables[0] == undefined) {
this.memory.state = this.states.harvest.code;
this.say('harvest');
}
}
},
}
}
module.exports = extended_creep;
var extended_creep = {
states: {
harvest: {
code: 0,
run: function (room) {
if (this.harvest(room.sources[0]) == ERR_NOT_IN_RANGE) {
this.moveTo(room.sources[0], { visualizePathStyle: { stroke: '#ffffff' } });
}
},
transition: function (room) {
if (this.carry.energy == this.carryCapacity) {
this.memory.state = this.states.load.code;
this.say('load');
}
}
},
load: {
code: 1,
run: function (room) {
if (this.transfer(room.loadables[0], RESOURCE_ENERGY) == ERR_NOT_IN_RANGE) {
this.moveTo(room.loadables[0], { visualizePathStyle: { stroke: '#ffffff' } });
}
},
transition: function (room) {
if (this.carry.energy == 0 || room.loadables[0] == undefined) {
this.memory.state = this.states.harvest.code;
this.say('harvest');
}
},
},
}
}
module.exports = extended_creep;
var extended_room = {
get: function(room){
let hostiles = room.find(FIND_HOSTILE_CREEPS);
let loadable_spawns = room.find(FIND_STRUCTURES, {
filter: (structure) => structure.structureType == STRUCTURE_SPAWN &&
structure.energy < structure.energyCapacity
});
let loadable_towers = room.find(FIND_STRUCTURES, {
filter: (structure) => structure.structureType == STRUCTURE_TOWER &&
structure.energy < structure.energyCapacity
});
let loadable_extensions = room.find(FIND_STRUCTURES, {
filter: (structure) => structure.structureType == STRUCTURE_EXTENSION &&
structure.energy < structure.energyCapacity
});
let loadable_containers = room.find(FIND_STRUCTURES, {
filter: (structure) => structure.structureType == STRUCTURE_CONTAINER &&
structure.store['energy'] < structure.storeCapacity
});
let loadable_storages = room.find(FIND_STRUCTURES, {
filter: (structure) => structure.structureType == STRUCTURE_STORAGE &&
structure.store['energy'] < structure.storeCapacity
});
let loadables = [...loadable_spawns,
...loadable_towers,
...loadable_extensions,
...loadable_containers,
...loadable_storages]
console.log('module extended.room - loadables: ' + loadables.slice(0,3));
let sources = room.find(FIND_SOURCES);
console.log('module extended.room - Sources: ' + sources);
let buildables = room.find(FIND_CONSTRUCTION_SITES);
console.log('module extended.room - Buildables: ' + buildables.slice(0,3));
return _.extend(room, {
sources,
loadables,
buildables,
hostiles
})
}
}
module.exports = extended_room;
var extended_creep = {
states: {
harvest: {
code: 0,
run: function (room, status) {
if (this.harvest(room.sources[0]) == ERR_NOT_IN_RANGE) {
this.moveTo(room.sources[0], { visualizePathStyle: { stroke: '#ffffff' } });
}
},
transition: function (room, status) {
if (this.carry.energy == this.carryCapacity) {
this.memory.state = this.states.upgrade.code;
this.say('upgrade');
}
}
},
upgrade: {
code: 1,
run: function (room) {
if (this.pos.getRangeTo(room.controller) > 2) {
this.moveTo(room.controller, { visualizePathStyle: { stroke: '#ffffff' } });
} else {
this.upgradeController(room.controller);
}
},
transition: function (room) {
if (this.carry.energy == 0) {
this.memory.previous_state = this.states.upgrade.code;
this.memory.state = this.states.harvest.code;
this.say('harvest');
}
}
},
}
}
module.exports = extended_creep;
var extended_creep = require('extended.harvester');
var creeps = {
get: function (creeps, name) {
let filtered_creeps = {}
for (let creep_name in creeps) {
if (creep_name.startsWith(name)) {
filtered_creeps[creep_name] = _.extend(creeps[creep_name], extended_creep);
}
}
return filtered_creeps
},
manage: function (creeps, room) {
let current_status = {};
current_status.tot = 0;
for (let [key, state] of Object.entries(extended_creep.states)) {
let tot = _.filter(creeps, (creep) => creep.memory.state == state.code).length;
current_status[key] = tot;
console.log('module harvesters - ' + key + 'ing' + ': ' + tot);
}
for (let [key, number] of Object.entries(current_status)) {
current_status.tot += number;
}
for (let [key, creep] of Object.entries(creeps)) {
this.run(creep, room, current_status)
}
},
run: function (creep, room, current_status) {
if (!creep.memory.state) {
creep.memory.state = creep.states.harvest.code;
}
for (let key in creep.states) {
if (creep.memory.state == creep.states[key].code) {
creep.states[key].run.call(creep, room, current_status);
creep.states[key].transition.call(creep, room, current_status);
break;
}
}
}
}
module.exports = creeps;
var extended_room = require('extended.room');
var harvesters = require('harvesters');
var builders = require('builders');
var upgraders = require('upgraders');
var spawner = require('spawner');
module.exports.loop = function () {
var creeps_conf = [
{
name_suffix: 'harvester',
tot: 5,
bodies: [WORK, CARRY, MOVE],
manager: harvesters
},
{
name_suffix: 'builder',
tot: 5,
bodies: [WORK, CARRY, MOVE],
manager: builders
},
{
name_suffix: 'upgrader',
tot: 5,
bodies: [WORK, CARRY, MOVE],
manager: upgraders
},
];
// CLEAN MEMORY FROM DEAD CREEPS
for(var name in Memory.creeps) {
if(!Game.creeps[name]) {
delete Memory.creeps[name];
console.log('module main - clearing non-existing creep memory: ', name);
}
}
var room = Game.rooms.sim;
var spawn = Game.spawns['Spawn1'];
var eroom = extended_room.get(room, 300000);
if (eroom.hostiles.length) { room.controller.activateSafeMode(); }
spawner(Game.creeps, eroom, spawn, creeps_conf);
for (let conf of creeps_conf) {
let ecreeps = conf.manager.get(Game.creeps, conf.name_suffix);
// creeps.spawn(ecreeps, eroom, spawn, conf.bodies, conf.tot);
conf.manager.manage(ecreeps, eroom);
}
console.log('module main - total: ' + Object.keys(Game.creeps).length);
console.log('module main - hostiles: ' + eroom.hostiles.length);
}
var spawner = function (creeps, room, spawn, creeps_conf) {
let current_number = {}
for (let conf of creeps_conf) {
current_number[conf.name_suffix] = _.filter(creeps, (creep) => creep.name.startsWith(conf.name_suffix)).length;
if (current_number[conf.name_suffix] < conf.tot && room.energyAvailable == room.energyCapacityAvailable) {
let name = conf.name_suffix + Game.time;
console.log('module spawner - spawning:' + name);
spawn.spawnCreep(conf.bodies, name);
break;
}
}
}
module.exports = spawner;
var extended_creep = require('extended.upgrader');
var creeps = {
get: function (creeps, name) {
let filtered_creeps = {}
for(let creep_name in creeps) {
if (creep_name.startsWith(name)) {
filtered_creeps[creep_name] = _.extend(creeps[creep_name], extended_creep);
}
}
return filtered_creeps
},
manage: function (creeps, room) {
let current_status = {};
current_status.tot = 0;
for (let [key, state] of Object.entries(extended_creep.states)) {
let tot = _.filter(creeps, (creep) => creep.memory.state == state.code).length;
current_status[key] = tot;
console.log('module upgraders - ' + key + 'ing' + ': ' + tot);
}
for (let [key, number] of Object.entries(current_status)) {
current_status.tot += number;
}
for (let [key, creep] of Object.entries(creeps)) {
this.run(creep, room, current_status)
}
},
run: function (creep, room, current_status) {
if (!creep.memory.state) {
creep.memory.state = creep.states.harvest.code;
}
for (let key in creep.states) {
if (creep.memory.state == creep.states[key].code) {
creep.states[key].run.call(creep, room, current_status);
creep.states[key].transition.call(creep, room, current_status);
break;
}
}
}
}
module.exports = creeps;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment