Bots as Fake Players — part 1

For multiplayer games, one of the biggest problems is bootstrapping. Even if someone discovers the game, dropping into an empty arena is not very fun gameplay.

There’s two different ways of approaching the same problem – get more real players, or improve the experience for when no real players are available.

If your game’s a hit, or you’re comfortable advertising as-is, then the first is usually the way to go. While developing – especially locally – that may not always be reasonable. I’ll be focusing on the later approach – how do you make a multiplayer game fun when no players are available? Well, let’s make our own players!

For a multiplayer game, the interface between client and server is (hopefully) very well defined.

In an ideal world, the client is purely a terminal – it renders the state that the server continuously feeds it. Trusting the client to maintain state has a lot of implications regarding cheating, so sidestepping that is usually preferred. If this is the case, the interface is the network – the client sends messages regarding their intent, and the server communicates the results of those actions.

Incidentally, there may be some issues with latency in this model – if your players are far enough away from the server it may take hundreds of milliseconds to see the results of their inputs. This is noticeable to the human eye.

So let’s say we wanted to create a very basic bot — one way of doing so is just taking your existing networking code and invoking it exactly as a client would. With this approach, you can have your bot logic actually react to the exact same data that the players would see, and send commands in exactly the same way a real player would. This means that theoretically the best bot would never do anything that a real player would not able to. In practice, humans do have a specific reaction time, so depending on the game a bot may have to be artificially limited in some way to approach human skill.

The other approach to bots is to inject their logic directly into the game code server-side. This is what I chose for this specific project, since it required less moving parts overall.

In my game loop, I have a function called ‘tick’ that the server makes sure is called once per logical frame of execution. All game logic is done through this function, to maintain fairness. When a player sends a game action, their intent is noted and associated with them, but the state does not actually get modified against the next tick. No matter how fast a player sends commands, they will be limited to the same speed as anyone else.

In order to introduce bots to the game, I defined a module with two functions – ‘newBot’ and ‘processBot’.


var unid = 0;
var memory = {};

exports.newBot = function(field) {
var id = "Bot" + (unid++);
memory[id] = {state: 'idle', fairness: Math.random()};
return {
nick: "PudgeBot" + unid,
id: id,
};
};

This function is called whenever the game decides it wishes to create a bot. I reused the same interface that a normal player would be using from the connection protocol I defined, except the bot response includes a specified ‘id’ value. (Normal connected users use the socket ID defined directly by Socket.io).

‘memory’ is a way to let bots keep track of some state for themselves – which player they’re targeting, where they want to be going, and what they want to do


// Returns an intent structure if it wishes to change what it's doing.
exports.processBot = function(vision, id, p, i) {...}

The second function I defined is invoked by the ‘tick’ method every frame for all bots. Some of the arguments are pretty straightforward – id is the bot id, p is the current player state, i is the current intent, but the ‘vision’ variable was something I needed to add in order to let a bot have a way to see what is going on.


// Object to be used for bots.
var vision = {
nearby: function(personId, pos, maxDistance) {...}
};

This function interacts with the rest of the game and returns a set of the nearest players to a given position. Coming from a Java day job, being able to pass function pointers around is very nice.

With a combination of a simple ‘process’ function and hooking the current game code to create bots when there aren’t enough players, this was enough to implement functionality that is almost as good as many players. I’ll go into detail on the actual behaviours that bots replicate in a later post.

Come try out the bots and play at http://ganks.io:7777/! (I have not set up an actual proxy yet.)

Leave a Reply

Your email address will not be published. Required fields are marked *