Choreographing Behavior: 2 examples

Original Author: Rune Vendler 

In this article, I am going to describe two older games that feature behavior systems (or whatever they should be called) that I like and find interesting. Both systems address the gray area where AI meets scripting, and offer solutions tailored to their respective technologies and game design. There are of course many more details to these games than I can cover here, so bear with me when I simplify or leave out details here and there. (I originally had a third example too, from a more modern game made by my company, but I did not get legal clearance at this time to post it.)

#1 Doomdark’s invasion plan

In Lords of Midnight (Mike Singleton, 1984), an epic battle for control over the land of Midnight is waged. In the southern half of the country, the Lords of the Free are holed up in their keeps and citadels. In the northern half of Midnight, the evil Witchking Doomdark has amassed a huge army and is now starting his invasion of the Free. Starting with a few key characters under your control, your task is to unite the Lords, defend the south, and eventually defeat Doomdark.


Lords of Midnight, in all its 8-bit glory

Lords of Midnight was a cross between an adventure game, a role-playing game, and a strategy game, offering multiple ways to be completed, and many ways to play it. It is probably best remembered for its “Landscaping” graphics technology which built static world views out of billboards, but in this article, I am going to take a look at something else: how Doomdark’s armies move south, conquering the fortresses on their way, and ending up at the citadel of Xajorkith for the final siege.

The land of Midnight

(Map originally from Crash magazine, found on the internet)

The Doomguard

Doomdark has at his disposal 128 regiments, the Doomguard. Each regiment starts at a specific hardcoded map location and with a specific activity already assigned to it. There are four types of activities:

  1. Follow a specific character – these regiments move towards the current locations of specific characters in the game.
  2. Wander – these regiments just wander around.
  3. March to location – these regiments march towards a particular map position as long as that location has its “interesting” flag set. If it’s cleared (e.g. all key characters leave that location), they will stop in place. (If the flag is set again, they will continue their movement towards the location.)
  4. March to waypoint – these regiments make up the majority and are the fun ones that I am going to talk about.

In addition to their specified activity, all regiments are able to react to something happening close by: if they spot an enemy within a certain distance, they will temporarily suspend their goal to engage them, and so on.

Waypoints

So, when the game starts, the regiments of type 4 start marching toward their respective target waypoints, reacting to any Free armies they pass underway. Most waypoints are locations of Free keeps or citadels, so there is likely to be a battle once they get there. What happens once the battle is over, and the regiment has reached the waypoint?

Each waypoint holds indices to two other waypoints (112 in total, IIRC), and the regiment randomly picks one of the two and starts heading towards that new waypoint. That’s pretty much it. This graph of waypoints is the essence of the invasion plan. It was created by hand and hardcoded into the game. I did a quick plot of it, and here’s what it looks like:

The black blobs are the waypoint nodes, and the arrows indicate two other waypoints that the node connects on to. Notice that a waypoint can have one or two of these indices pointing back to itself (shown by a number inside the waypoint indicating if one or two of the indices loop back). This can be used to “park” regiments at strategic locations once captured. Here is the waypoint data for Doomdark’s home citadel:

Waypoint #6, name=”The Citadel Of Ushgarak”, position=”29,8”, connections=”6,14”

So, an army arriving at Ushgarak has a 50% chance of staying for another day (if it picks the first connection), and 50% chance of heading for waypoint 14. That’s pretty much it! The behavior of the regiments are a function of their base AI and this graph. The random choice of next waypoint guarantee that the game will be subtly different on every playthrough. Hack the waypoint data, though, and you can create a very different invasion!

#2 Tactics in SWOS

Sensible World of Soccer (Sensible Software, 1994) is a much celebrated football/soccer game that has seen many revisions and remakes over the years. While it is very action-focused and fast paced, it also manages to have a tactical side to itself, and a key component of this is the team tactics. Players choose a tactic for their team, e.g. a 4-4-2 formation, and this tactic defines how the different characters will position themselves and move around the field. Here’s how it works:


The soccer pitch, with the tactics areas overlaid

The pitch is divided into 5*7 = 35 areas, and for each field player on the team (10 in total), the tactic contains a table for where he should be when the ball is in each of these areas, i.e. “if the ball is in area N, this player should be at position X,Y”. So a tactics file is 10 players * 35 entries for each player. We can move the players around the field with this algorithm:

  1. Figure out which area the ball is in
  2. For each player,
    1. Look up his target position for when the ball is in that area
    2. Move him towards that position

This can then be supplemented with some AI that overrides the player’s actions when he is close to the ball (e.g. seek to control it), in possession of the ball (e.g. either move towards goal, shoot at goal, or pass to another player), or close to an opposing player who has the ball (e.g. tackle).

Demonstration

I put together a quick demo to show the system in action. Here’s a YouTube video showing it:

Code by me, all graphics found on the internet

Here’s what you’ll see:

  • A ball – this is moved around by me with the keyboard.
  • Field players – they will run towards their target positions, found with the algorithm above and indicated by a white blob connected to them by a line. (Often, a player will be on top of their blob, in which case you can only see the player.) The red team’s home is at the bottom, the black team at the top.

In sequence,

  1. A single player being driven around by the ball.
  2. The same player, but with bilinear sampling of the tactic data, so his target position moves smoothly instead of jumping when the ball changes area. (I keep filtering on for the rest of the video.)
  3. A full team moving around
  4. Two teams both moving around.

Both teams play the 4-4-2 tactic, so when the ball is around the middle of the field, you can see the symmetry. (Apologies for this, it might have been more fun with varied formations.)

The code driving the players around is not much more than 20 lines. Creating a tactic in the SWOS editor is certainly a bit of work, but look on the internet, and you’ll find lots of people creating custom tactics.

Why do I like these systems?

I think I like these two systems because:

1. They use very little abstraction.

We can probably all agree that unnecessary abstraction is a bad thing, but it’s a lot harder to agree on what exactly meets that criterion, and thus interesting to see how different developers have approached the matter. Both solutions are fairly specific: they solve only the problem they need to, within the domain at hand. To me, they very much come across as systems focused on their end result rather than the potential AI exercise.

2. They are simple

I think both systems are examples of combinations of simple code with clever (but equally simple) data. You can type up a test-implementation in no time, and because of the low level of abstraction (leading to very limited hidden state), you can get pretty well defined results with a minimum of debugging.

3. Their real power is in their data

A lot of the perceived intelligence and complex behavior resides in the data, so to speak, not in the algorithm itself. This is great, because it allows us to play with the data and observe the differing results. This ability to choreograph the behavior through “painting the data” is a great feature of the systems, as it’s one way to get around the friction that often occurs when you need to script some part of an (in principle) autonomous system.

4. They are fun to work with

Perhaps not a particularly highbrow point, but they are systems that it’s fun to think about, expand on, code up and play around with. :)

I like looking at these old games. There is often an element of nostalgia involved when looking back at classic games written on simpler technology, and perhaps a tendency to consider the technical (and design) solutions of those games “hacks” when viewed in light of our processing power today. There is a lot of good stuff in there, though, and can often be applauded for their effective and economical approaches, and it can be worth it to spend some time taking your favourite classics apart!