If you're new to programming, or you're new to JavaScript, but you think Screeps is cool and you really want to play it, in this tutorial I'm going to show you the simplest Screeps code possible and I'm going to walk you through step-by-step what each line of code means, what it does, and how I got there. Super beginner friendly is my goal here.
Here's the code we'll end up with:
module.exports.loop = function () {
// This is the main game loop. The code inside of these curly brackets will run once per tick.
// if our creep doesn't exist, create it from our spawn
Game.spawns["Spawn1"].spawnCreep([WORK,CARRY,MOVE,MOVE], "My First Creep");
// get a reference to our creep
var mycreep = Game.creeps["My First Creep"];
// if creep has no energy, go to the energy source and harvest some
if (mycreep.store[RESOURCE_ENERGY] == 0) {
// make an easy reference to the energy source
var source = Game.getObjectById('16c3f93dd468ca9f065fd27c');
// move my creep to the energy source and harvest energy
mycreep.moveTo(source);
mycreep.harvest(source);
} else {
// but if our creep does have energy, bring it to the controller and upgrade it
// make an easy reference to the room's controller
var controller = mycreep.room.controller;
// move my creep to the controller and upgrade it
mycreep.moveTo(controller);
mycreep.upgradeController(controller);
}
}
To get started for this tutorial we just need to go into the Screeps simulation room.
So go to Screeps.com, scroll down to the "Live Demo", and for "Simulation Mode" select "Training". This will bring you into the training room for Screeps (https://screeps.com/a/#!/sim/survival). You don't even need to register or anything to follow along here.
The first thing it's asking you to do is place your spawn. This is a building that's going to create creeps for you. So go ahead and place it somewhere near this energy source and near your controller. And you can just keep the default name, "Spawn1".
Then in the lower left click "Script" to open up your code. Go ahead and expand it up so you can see more of it. So the first bit of code that they give you when you're starting out is this module.exports.loop
equals some function. So what this is, it's the main game loop that gets exported and run by the Screeps server. Any code you put inside of this function that's being exported will be run once per tick.
So anything you want to do with your creeps or your structures, all that code needs to go within these two curly brackets, or at least be initiated from here. And you don't need to fully understand how all this is working to get started. Just think of it as boilerplate code that needs to be there in your main script, and remember that the code you want to write all needs to go in here.
The first line of code they give you inside the main game loop is actually just a comment. You create comments in JavaScript by using these two slashes, and they allow you to leave notes inside your code. And you can write whatever you want inside a comment, they're ignored when your code is executed. So this is your space to take notes, to give yourself reminders about what you're doing. And every programming teacher ever is going to tell you to leave lots of comments, because it's really good advice. So let's put a comment here to remind us about how the main game loop works.
// This is the main game loop. The code inside of the curly brackets will run once every tick.
And while we're talking about ticks, in the upper right here you can control the speed of the ticks inside the simulation. You can pause the game, or you can change the tick speed to speed up or slow down the game.
Now, before we start writing any code, let's just first talk conceptually about what we want to do to write the simplest Screeps code possible.
So right now, all we have is a spawn, and a spawn can be used to create creeps. And the object of the game is to go and harvest energy, from an energy source, and we want to take that energy and use it to upgrade our room controller. And as we upgrade our room controller to higher levels, it will give us the ability to build new types of structures and to create stronger creeps.
So to do that, we need to, from our spawn, create a creep. Then we need to program our creep to go over to the source, get some energy from it, and then take that energy over to the room controller and upgrade it. And then we want our creep to just go back and forth doing that process forever.
So now that we've talked about what we want our code to do, the next thing I like to do, when I'm writing something that's new or complex for me, I just like to outline my logic with comments first. So this isn't any code that I'm writing, this is just notes to myself about what I want my code to do. This breaks everything down into smaller chunks that I can then tackle one at a time.
So the first thing we want to do is we want our spawn to create a creep if it doesn't exist.
// if our creep doesn't exist, create it from our spawn
Ok, so once our creep exists, it's going to be helpful to get a reference to that creep, because we're going to use that object a lot as we're telling our creep what to do. This way we don't need to look it up every time.
// get a reference to our creep
So we've created our creep, and we've got an easy reference to it, the next thing we want to do is have our creep perform its logic. And the logic for this creep is, if it doesn't have any energy we want it to go to the energy source and harvest energy.
// if creep has no energy, go to the energy source and harvest energy
But if our creep does have energy, we want it to take that energy to the controller and upgrade it.
// but if our creep does have energy, bring it to the room controller and upgrade it
Alright, so now we've outlined our logic, and we know if we can just accomplish these four things we will have our fully-functioning Screeps code. So now all we have to do is just fill in the blanks.
Let's start with the first one, "if our creep doesn't exist, create it from our spawn". Ok, so how are we going to do that. Well, we know that we have our spawn here, and we know that it must have some way to create a creep. So let's go over to the documentation to see how we would do that.
I'm just going to jump right to the "API Reference". And then I want to go to the structures, and look at the spawn structure. So the class is called StructureSpawn
. Here it says, "Spawn is your colony center. This structure can create, renew, and recycle creeps. All your spawns are accessible through the Game.spawns hash list."
So inside StructureSpawn
, it's probably going to have some sort of method that allows us to create a creep. The green items here are all properties, that's just data that belongs to each one of our spawn structures. And the items in blue are all methods. And methods on an object allow us to perform some sort of action. So if we keep scrolling down, you'll eventually get to spawnCreep
. This is the method we are looking for.
So let's copy the spawnCreep
function definition, and go ahead and paste it into our code right below the comment where we say we want to create a creep from our spawn. The square-bracket [opts]
here is just optional parameters, so we don't need it, and you can just delete it. Now our spawnCreep
function takes two arguments: it wants a body definition and a name for this creep.
Looking back at the documentation, we can see that the body needs to be an array of body parts. This is a list of strings that describe our new creep's body. So basically every creep is made up of different parts, and you get to decide what your creep is going to look like as far as what body parts it has. And the more parts it has, the more expensive it is to create.
If we go back and look at our spawn, you can see that it's energy is 300 out of 300. So it has 300 out of a maximum of 300 energy. And we can use this energy to create our creep. But we won't be able to create a creep right now that costs more than 300 energy. We'll come back to this in a minute.
Back in our code, let's create an array that defines our body. And arrays are defined using square brackets, so everything inside these square brackets is going to be something in our array. And an array is just a list of data. In this case we want our array to contain the different parts that will make up our creep. And the available parts are all listed here in the documentation. We only need to focus on the first three: the WORK, MOVE, and CARRY parts.
We do need at least one WORK part, both to harvest energy from a source, and also to upgrade our controller. So let's start by giving it one WORK part. "WORK" needs to be in all caps because this is a constant that Screeps defines, and if you don't put it in all caps exactly how Screeps defined it, it won't recognize what you mean.
And we always separate the different items in an array with commas. So following our WORK part we also need a MOVE part. This allows our creep to move.
And the one final thing we need is, we need a CARRY part, which will allow our creep to carry energy. Which is something we need it to do, to bring energy from the source to the controller.
So we've got a WORK, a MOVE, and a CARRY part on our creep. Hopefully that costs less than 300 energy. We can confirm that in the documentation, if we look at the "Constants". This is where those constants are defined. So the WORK, the MOVE, and the CARRY are defined in these constants. And this lists out all the constants in the game. You can see here, MOVE, WORK, and CARRY just correspond to these strings. So you could use those strings instead of the constants if you wanted, but it's better to use the constants in case these values ever change.
And, right below these, we have BODYPART_COST, which tells us exactly how much energy each one of those body parts costs. So we used 1 WORK, which costs 100. We used a MOVE which is 50, and we used a CARRY which is also 50. So 200 energy is how much our creep will cost to spawn, as we've defined it right now.
But if you read the documentation further, you'll find out that, the way these MOVE parts work is, if you want your creep to be able to move on every tick, you need the same number of MOVE parts as every other type of part. So let's give it a second MOVE part. That way we've got two MOVE parts to outweigh our two other parts. And we know each MOVE part costs 50, so in total we've got 250 energy required to make a creep with this body. And we have 300 energy available to us, so we're good there.
Let's look back at spawnCreep
. The name parameter, you can see here should be a string, and it should just be the name of a new creep. And "It must be a unique creep name". That's because we can reference our creeps by their name, so there can't be any overlap in creep names or that lookup would be ambiguous. So let's give our creep a name. Strings in JavaScript are enclosed with quotes, and the syntax highlighter here will turn them green. So you can call it whatever you want in here, but I'll call mine "My First Creep"
.
And then every statement in JavaScript should end with a semicolon. That lets the JavaScript interpreter know that this is the end of that line. This is useful in case a line of code gets really long, you can actually break it into multiple lines without causing any errors because the interpreter knows to just look for the semicolon at the end.
Now in order to save code and have it start reflecting inside the game, you just click this checkmark over here to commit your changes. And if there's any problems with your code it will show up over here in the console, and it gives us this red warning symbol to let us know something's gone wrong. So if I click on the console, we can see these errors popping up, every tick, that says "spawnCreep is not defined". So I'll go ahead and pause the game so we don't get a huge list of errors.
And the "spawnCreep is not defined" is the key part of this error message. It's saying that this spawnCreep
function we're trying to call, it doesn't know what we mean by that. And it makes sense if you think about it, because we haven't told it what spawn building we want to create the creep from. The spawnCreep
function is actually attached to an object, and specifically it's only attached to the StructureSpawn
objects.
So the second part of the task here is to figure out, how do we get a reference to this spawn that we want to create a creep from. Let's go back to the documentation, and I want you to look over here at the global objects. The global objects contain all the data about the state of the game as a whole. And the nice thing about these global objects is, they're available anywhere inside of your Screeps code. You always have access to them.
And the big one that contains most of the information about what's going on inside the game is just the Game
object. So I'll open that documentation. And as you scroll down you'll see that the Game
object has all kinds of different properties on it. And the one we're interested in now is the spawns. So spawns is "a hash containing all your spawns with spawn names as hash keys."
So the way that hashes work is, they're like arrays in that it's kind of like a list of data, but instead of it being an ordered list of data, each piece of data inside a hash is actually assigned to a key. And you use that key to access the data you're looking for.
So actually just down here there's a good example of a hash (in Game.cpu.getHeapStatistics
). You can see this hash is defined inside of curly brackets. If this were an array, it would be square brackets instead. And inside, these strings like "total_heap_size"
, that is a key, and the data that's being held at that key is this number to the right of it, after the colon.
So what this all means is, in the Game.spawns
property, the spawn we're looking for is going to be keyed by the name we gave it.
So back in our code, we can type "Game", with the 'G' capitalized, and that refers to the global object that's available to us. And we know the Game
has a spawns
property. You access properties and methods on an object using a dot, or period, and you can see the auto-complete here is even working for us as I type "spawns".
We just want to get a reference to this spawn right here, and the way that you access a value inside of a hash, in JavaScript, is again with square brackets. And inside the square brackets we need to give it the key, which is a string, and in this case the string should be the name of the spawn. Our spawn name is "Spawn1"
, which you can see over top the spawn. You can also see it over here on the right, the name of the spawn is "Spawn1". So I'll just copy that and paste it in as our string.
So the key we're accessing in our spawns hash is "Spawn1"
.
And after referencing that spawn we need another dot. That's because Game.spawns["Spawn1"]
is going to evaluate to a StructureSpawn
object, and spawnCreep
is a method belonging to that object. These dots are used to separate properties or methods from the object they belong to.
So to break this down one more time, Game
is a global object. spawns
is a property existing on that object (so we use the dot and then spawns
). And spawns
itself is a hash that uses spawn names as the key. So we access the information in that hash by giving it the key with our spawn name. So this first portion of the code is going to evaluate into a StructureSpawn
object referencing our spawn. Then from this StructureSpawn
object, we want to call the spawnCreep
method on it. And as we discussed earlier, spawnCreep
takes these two arguments. And you can go back and verify all this in the documentation if that's an exercise you'd find helpful.
So we've finally arrived at the working code for creating a creep. And I know it seems complicated at this point, but this really is the best way to keep code organized and sensible. It can seem overwhelming at first, but if you can get your head wrapped around how each piece of data you need, and each method you need to call, is something belonging to an object, and those objects are often encased in other objects, then you'll be well on your way to not only understanding Screeps, but to understanding programming in general. This is what we mean by object-oriented programming.
And if you were clever as we were looking at the documentation, you might have noticed, to the right of this spawnCreep
documentation, they actually give you examples of how to use it. And this first example is actually very similar to the solution we ended up with to spawn a creep.
So now if we commit this code, and run the simulation again, now you see our spawn is doing something. This line that's going around the spawn indicates that it's creating a creep. The more parts you have on your creep the longer it takes your spawn to create it. You also see that the yellow circle has shrunk on the spawn. That's because it used up 250 energy to create this creep. On the right you can see that the energy in the spawn has decreased, and it's gaining back one energy every tick.
And now that our spawn has finished creating our creep, it's popped out here and it's just sitting there, waiting for us to tell it what to do.
One thing to note before we move on is, remember I told you this game loop is running once every tick. That means every tick of the game it's calling this spawnCreep
again. Which could be problematic, because you'll notice from our comment we only want to create our creep if it doesn't exist. But because each of your creeps must have a unique name, and we're giving it this same name every time, what's happening is this spawnCreep
function is recognizing that a creep already exists for this name, so it's not creating another one.
So spawnCreep
is actually returning a non-fatal error, and that's not being reported to the console. But if we did look at the spawnCreep
method yet again, you can see that it actually has a return value. So when we do create the creep successfully, it's going to return this OK
constant. But every time we call the spawnCreep
method and our creep already exists, it's going to be returning this ERR_NAME_EXISTS
value.
And those return values are there so that we can have some way of knowing if the spawnCreep
function failed or not. And if we wanted to look at what that return value was we could create a variable to store it. And you would do that like var
for variable, name it result
, and then assign it to whatever spawnCreep
returns. And then result
would hold one of these values every tick.
In our case, right now, it would be holding ERR_NAME_EXISTS
every tick, and the raw value of that constant is -3
.
But that's getting off-track a little bit. It's perfectly fine just to ignore the returned value and don't do anything with it. And your code will still work just fine.
So the next thing we want to do is we want to get an easy reference to our creep. And this part isn't absolutely necessary, we could keep referencing our creep from the Game
object, but because we're going to be using it a lot, it is helpful to create a variable that will just hold a reference to that object.
So let's create a variable, again, var
to do that, and this is your variable name, you can call it whatever you want. I'm going to call it mycreep
. And when you create a variable, usually you want to put some sort of data inside that variable right away. When you declare a variable like this you're basically asking for a little bit of room in memory to store something. And what we want to store in here is our creep object. Specifically the "My First Creep"
object.
So to get that reference we're going to, again, look at the Game
object. Let's go back to the documentation so we get used to doing that. Inside Game
, we do have a creeps hash. "This hash contains all of your creeps." So Game.creeps
, and we know how to access a hash. You use the square brackets, and inside those square brackets you give it the key you want. In our case the key we want to use is "My First Creep"
, because that is the name of the creep we want to get a reference to. And then don't forget the semicolon at the end of the statement.
We'll commit that. Check to see if there are any errors coming out of the console. I'll clear this console so we don't see those old errors. And everything looks fine with our code right now.
If we want to confirm that we have the correct reference to the creep we believe we do, on mycreep
, which is a creep object… let's look at what creep objects can do. Click on Creep in the documentation. Of course they have lots of different properties and methods. One thing they can do is they can say
. If you call the say method on your creep, and you give it some text to display, it will visually display what you want that creep to say. So let's do that.
So mycreep.say()
, because that's a method on all creep objects. And we can just give it some message, I'll say "I live"
. Remember the semicolon. Let's commit that.
Now our creep is saying "I live" every single tick. So we do indeed have a proper reference to that creep.
This is a bit of debug code that we don't need anymore, so I'm going to go ahead and delete it. If you wanted to disable it without deleting it completely, you could instead comment it out by adding two slashes to the front of the line. And if we commit this our creep will stop talking.
So now, let's go back and re-read the next step here. "If creep has no energy, go to the energy source and harvest it."
So the first part here, "if creep has no energy". This is a very obvious use-case for an if
statement. An if
statement in JavaScript allows you to execute a bit of code only under certain conditions. So if
(all lowercase, that's a keyword in JavaScript), and then we're going to use parentheses. And inside the parentheses goes the condition you want to evaluate.
So in this case we want to check to see if the creep has no energy. We can assume it's going to be mycreep
that contains the data we need, and we want to see how much energy mycreep
has. So we could go to the documentation, and it might not be immediately obvious what you're looking for, but it's actually store
.
store
is a property on every creep. And it contains a hash of every possible resource in the game your creep could be carrying. So mycreep.store
is the property we want. We're accessing a hash so we use square brackets. And the thing we want here, is we want to check how much energy it's carrying. This key is going to come from the constants. There's a constant for RESOURCE_ENERGY
, and that is the one we're looking for. And you would know that either from doing the in-game tutorial, or reading the gameplay articles in the documentation.
So mycreep.store[RESOURCE_ENERGY]
, this entire bit of code, is going to evaluate to a number that represents how much energy this creep is carrying. So right now our creep is carrying 0 energy, and you can see that over on the right that the carry is 0 out of 50, and it says "Empty". So this whole bit of code is going to evaluate to 0 right now, but as our creep starts to harvest energy this value will go up.
For our if
condition we just want to check to see if it is 0. So if the creep's stored energy is 0 we want to do something, specifically here we want to go to the energy source and harvest energy. So first we have to check to see if it's equal to 0. In JavaScript, and in a lot of programming languages, you use the double equals sign to check for equality. So we'll see if it's equal to 0. Now we have a true or false statement that's appropriate for an if
condition.
Now that we have that, we want to do something just in this case where the creep is carrying 0 energy. And the syntax for if
statements is the parentheses part, which is the condition we're checking, and that's followed by curly brackets. And everything inside these curly brackets is what's going to be run only in the case where this statement is true.
So inside these curly brackets is where we want to go to the energy source and harvest the energy. I'll move a little faster here. mycreep
has another method on it called moveTo
. And in here you give it the place you want to go, so we're going to call that source
, which doesn't exist yet but I'll come back to that in a moment. And then we also want to harvest
. And what do we want to harvest? We want to harvest the source.
So every tick where the energy carried by the creep is equal to 0, the creep is going to be told to move towards the source, and also harvest the source. Now you can only harvest a source when you're in range, so ticks where the harvest fails, this is actually going to return some sort of fail message. But just like before with the spawnCreep
, we can choose to just ignore the result that's returned. And that's not a problem. We won't get any errors in our console.
So stepping back now, before we can have our creep moveTo
and harvest
the source, we still need to tell it what the source is. So let's create a variable, we're going to call it source
, and in this variable we need to assign it this source right here.
Now your first instinct is probably to look again in the Game
object, and look for this particular source. And that's not a bad instinct. Let's go look at the Game
object. You can see we have all these different properties here, the creeps, the flags, the map (this might have what we want in it), the market, resources (you might be tempted to investigate this one, but I can tell it's not going to contain what we're looking for), rooms (you might suspect that maybe you're going to look inside this room, and that might have a reference to the sources). I think those are our candidates. So either rooms
or map
.
Let's take a look at the map
. So the map just has a bunch of methods, and it doesn't look like they're going to have the data that we want.
Let's checkout rooms
. In the room object, this does have a reference to the controller, which is nice because we'll need that next. But you can see it doesn't have any properties referencing the sources. It does have this find
method, that you can actually use to get a list of all the sources within a room, and then you can filter those to get the one you want. But you'll find this code is a little more complicated, you can even just peek here on the right to see it's a little more complicated, and there is actually an easier way to do it that I want to show you.
I want to draw your attention back to the Game
object. It has this method getObjectById
. The great thing about getObjectById
is that it's a really fast lookup. Everything inside the game of Screeps has a unique id, and you can use this to quickly get a reference to any object, whether it's a creep, or a structure, or something inside the room like an energy source.
So that's what I'm going to show you how to do here. Use the global Game
object, call getObjectById
on it, and all we need to do is pass in a string with the id of the thing we want to reference. So in the game, go ahead and select the source, and you'll notice right below the blue bar on the right where it says "Source", it says "id" and there's this long string. Go ahead and copy that. It's going to be different for you than it is for me. And we'll paste it in for our string. Remember the semicolon.
And this will now give us a reference to our source. This specific source, so not this one or this other source, but specifically this source. And once we have a reference to that object we can go ahead and use it to moveTo
and to harvest
.
And you can do this with anything else in the game, like the spawn will have an id, or creeps have an id. So if you prefered, you could use Game.getObjectById
to reference your creep, but when your creep dies, and creeps don't live forever, once the spawn recreates your creep by calling spawnCreep
, the new creep that gets created will have a different id. So I don't recommend doing that because your code will break after about an hour or so.
But in this case it's ok to use getObjectById
because we know this source isn't going to be destroyed or disappear, and it's always going to have this same id. So it's perfectly fine to use it in this situation.
So let's commit our code, and hopefully our creep will move over to the source and start harvesting it.
I'm going to speed up the ticks here. I find that the sim room can actually get slower if you go above 2 ticks/second, so I'll just leave it there.
And now that the creep's at the source, you should see that it starts to gain energy. And yeah, it did harvest once. And so in that one harvest we were able to get 2 energy from it. But now you'll notice that we're carrying 2 energy. In this if
statement that we wrote, our creep's energy store is now 2, which doesn't equal 0, so it's not entering this if
block. It's not running this code. That's because this whole condition inside the parentheses evaluates to false, so the code inside these curly brackets is never run.
So now in this situation, where we do have energy, we want to do what we wrote in this last comment. "But if our creep does have energy, bring it to the room controller and upgrade it." So we can accomplish this by adding an else
to our if
.
You do that by using the keyword else
, and then again the curly brackets to hold the code that we only want to run when the if
conditional is false. And I'm going to move this comment to be inside the else
block as well.
So if the statement inside this if
condition is true, it's going to run the code in these first curly brackets, else
, if it's not true, it's going to run the code inside these second curly brackets.
So here we want to listen to the logic we wrote in our comment. mycreep
dot moveTo
. In this case we want to move to our controller. Which, again, is a variable that doesn't exist yet. And then, on mycreep
you'll notice there's an upgradeController
, and you just need to give it the controller. Don't forget the semicolon. And that should be all the code we need to move to and upgrade the controller, once we define what controller is.
So we'll create a new variable, call it controller
, and we need to set it equal to the controller object inside this room. And you'll remember when we were looking at the Room object, it does actually have the controller object that we need as one of its properties.
So all we need now is to get the room object that our creep is currently in, and from that we can easily grab the controller object. And if you go back and look at Creep, Creep has a property called room
, which just links to the room object the creep is currently in. So the controller can simply be mycreep.room
(this will be the "sim" room that our creep is currently in), and then that object will have a controller
object inside of it. And so this whole bit of code will evaluate to this room controller object.
And this is one of the reasons I really like Screeps for teaching programming, because you can immediately relate a controller
object in code to a controller object you can see in the game visuals.
We'll go ahead and commit that, and now our creep has energy, so yeah it's going to go over to our controller and upgrade it. And once it gets back down to 0 energy, well the condition in our if
statement's going to be true again, so it's going to execute this code to harvest from the source. Until it gets some energy, then it's going to execute the else
code again.
And that's what it takes to write the simplest Screeps code possible. This code will run indefinitely. You could leave it running for days or weeks and you'll still have a creep running back and forth upgrading the controller with energy. And every time your creep dies, your spawn will simply create a new one.
So hopefully this was helpful. Hopefully you got a better idea of how Screeps organizes the data that you need into different objects, and nested objects, and how methods on those objects allow you to perform the necessary actions inside the game.
If you're new to programming, hopefully you got some sense for how to create a variable, what arrays are and how they work, same with hashes, what strings are, and hopefully you're going to remember always put that semicolon.
And hopefully you understand the curly brackets: where they go, and how they isolate bits of your code from other bits of your code.