If you’ve spent any time on slack you’ve probably seen players posting graphs of some part of their Screeps AI. The tool we use to do this is Grafana, a fantastic analytics and graphing program.

If you look at Grafana straight away you will see that the requirements are pretty hefty. A statsd and graphite server just to provide the data to Grafana, somewhere to host the dashboard. Let alone a system to pull data from Screeps into that setup.

Enter Screeps Plus

Screeps Plus is a free service that provides graphite and grafana hosting. The only requirement is that you have somewhere to run an agent which reports the data to Screeps Plus.

This agent sits on a server/computer that you’ve set up. This gives you control over the agents reporting and keeps your username and password private.

Screeps Plus has some pretty detailed instructions on how to set up the agent on its Github Repo.

Generating your first Stats

Now your agent is sending data over to Screeps Plus you need some data to graph. The best place to record your stats is at the end of your loop or when you’ve already calculated the number to keep CPU usage down.

To start with, create the Memory.stats object if it doesn’t exist. In theory 1 tick after you deploy this, it will only ever return false.

if(!Memory.stats){ Memory.stats = {} }

The keys in the stats object are passed into graphite which you can query in Grafana.

The simplest stats to gather are your CPU usage which can be done with these 3 lines.

Memory.stats['cpu.getUsed'] = Game.cpu.getUsed() Memory.stats['cpu.limit'] = Game.cpu.limit Memory.stats['cpu.bucket'] = Game.cpu.bucket

These numbers will then be saved at the end of the tick with the rest of your Memory.

To create a graph of your CPU usage as a percentage of your limit you need to setup your graphs metrics like so:

Your data comes over into screeps.username with then the keys from your stats object, in this case, CPU usage is screeps.username.cpu.getUsed. The graphite function of asPercent(#B) does what you’d expect and returns 0-100 for the percentage of the value of series B. Series B is the CPU limit from screeps.username.limit and in this graph I’ve hidden it.

With some tweaking of the Legend and Axes settings, you can get the above graph. The legend is set to table with the average, minimum, maximum and current values displayed. The Y axis is locked at 0-100 with its units set to %.

Setting the Time and Refresh Rate

Grafana can be set to refresh at given interval and to show a given range of time.

If you click on the time range in the top right corner you will then be given the time range and refresh settings.

I like to save my dashboard set at Last 1 hour and refresh every 10s. This is a matter of preference for you. shard0 ticks slower so the data won’t change much in 10s, equally shard1 ticks fast enough that 5s should show changes.

The time From and To boxes can be used to show your stats are a set time giving you the ability to see your stats during a battle etc…

Praise GCL

To graph your GCL you need 3 numbers. Your current level, the progress to the next level and the total progress needed for the next level.

Memory.stats['gcl.progress'] = Game.gcl.progress Memory.stats['gcl.progressTotal'] = Game.gcl.progressTotal Memory.stats['gcl.level'] = Game.gcl.level

I normally graph GCL in 3 single stat boxes as a full graph would be a waste, your GCL doesn’t go up fast enough for 1 hours graphing to be worth it.

One for my level, one for the GCL progress percentage the same as CPU and one with a prediction of the time left.

The first box just needs to display the current value of screeps.username.gcl.level.

The second is the same as the CPU box above but again only displaying the current value.

The third uses a feature of graphite for forecasting.

Series A takes the current progress towards the next GCL level and takes the moving average of the last 5 hours to stop the estimate from jumping all over the place when a single upgrader misses a tick. Using perSecond() gets the rate of change per second.

Series B is the difference between the current progress (#D) as the total progress needed.

Series C divides Series B by Series A to get the number of seconds it will take to level up. Passing this to holtWintersForecast() which smooths the results and gives an average answer.

Series D is the progress tot he next GCL level.

To get Grafana to give you the time in a human-readable format set the units to duration (s).

Per Room Stats

Some stats need to be generated per room. My AI takes a few per room stats.

_.forEach(Object.keys(Game.rooms), function(roomName){ let room = Game.rooms[roomName] if(room.controller && room.controller.my){ Memory.stats['rooms.' + roomName + '.rcl.level'] = room.controller.level Memory.stats['rooms.' + roomName + '.rcl.progress'] = room.controller.progress Memory.stats['rooms.' + roomName + '.rcl.progressTotal'] = room.controller.progressTotal Memory.stats['rooms.' + roomName + '.spawn.energy'] = room.energyAvailable Memory.stats['rooms.' + roomName + '.spawn.energyTotal'] = room.energyCapacityAvailable if(room.storage){ Memory.stats['rooms.' + roomName + '.storage.energy'] = room.storage.store.energy } } })

This for each room records:

The RCL level.

How much progress has been made to the next level.

How much it will take to upgrade.

How much energy is available to spawn creeps.

How much energy could be used to spawn if everything was full.

How much energy is in storage.

Before creating graphs for this its time to use another feature of Grafana, Templating variables.

Templating variables let you filter your queries or repeat pannels per entry in the variable.

To create a templating variable click the settings menu for the dashboard and select Templating. Add a new variable.

You should get a list of all your rooms in the preview. Once you hit Add you will get a new box at the top of your dashboard that lets you select rooms.

For each room we want to have:

A single stat box for its RCL level. A single stat box for its RCL progress. A single stat box for its RCL prediction. A graph of the spawn energy. A graph of the stored energy.

All of these should be easy enough to add using the same processes as before, the first 3 single stats are exactly the same as the GCL ones. The graphs are easy enough to figure out. Its just a case of repeating it per room.

Add a new row to your dashboard and go into its options. Hover over the left end of the row to bring out the menu.

This shows the row title and sets it to the value of $room which because of the repetition per room will be the name of the room this row is for.

When adding the boxes you need to substitute the room name for $room in the queries.

When you make changes to the first row you need to change your selection to 1 row and then back to normal to cascade the changes to every row.

More Graphs

If you can put the numbers into your stats you can graph it. Any other graphs will depend heavily on how your AI works.

For example, my AI works as an operating system so I graph the number of processes, suspend processes, and the CPU usage per process type.

There is a lot of scope in Grafana for colouring based on data and other visual features that make it easier to make sense of your data. I encourage you to explore all the options and see what you can make!

I hope this guide has helped you get started in the world of graphing.