Defconsts, Goals, and SNs - Part 1: The AI Engine is a Numberphile

By the end of this short article series, you'll know how defconsts, goals, and strategic numbers work and what happens behind the scenes in the AI engine when you use them. If you've ever felt nervous about using defconsts, goals, or strategic numbers correctly, or if you've often struggled to keep c:, g:, and s: straight when using Userpatch AI commands, this series is also for you.

How the AI Engine Translates Your Code Into Numbers

If AI scripting is your first programming language, you may have assumed scripting an AI is mostly word-based. Plug the right words for the things you want your AI to do into the correct commands, and the AI engine magically knows what those words mean. However, the AI engine doesn't actually know English at all. Instead, at its core the entire AI scripting language is all numbers. It's a Numberphile, and we have to cater to its whims. When the AI script is compiled at the beginning of the game and checked for errors, I believe the AI engine converts all of the AI code into strictly numeric code.

For example, take the code (players-unit-type-count any-enemy archer == 5). If you have any modding or AI experience, you may know that every object in the game is assigned a different object ID. If you ever want to look up what a unit's object ID is, you can look at this Object Table. Conveniently, the Archer unit is at the very top of this table (I certaintly didn't plan that), and its ID is 4. So, when this command is compiled, the AI engine replaces "archer" with "4" like this: (players-unit-type-count any-enemy 4 == 5). In case you were wondering, you can even write your AI code using "4" instead of "archer," and your code will run exactly the same.

But wait, there's still English in this code. Next, lets look at "any-enemy." any-enemy is one of the wildcard parameters of a player number. Wildcard means it's dynamic and it can refer to different player numbers depending on which wildcard parameter you used. any-enemy simply picks an enemy player number at random. You can see the full list of player number options on this page: Link. You'll notice that any-enemy has a player number ID of -106. Why is any-enemy -106? It's arbitary, but each player number option is given a different player number ID. Continuing our translation of our code into the numbers that the AI engine actually uses, our command now looks like this: (players-unit-type-count -106 4 == 5). And in case you're wondering, yes, you can always use -106 instead of any-enemy throughout your own AI code if you like, not that I'd recommend it. As you can tell, replacing your AI code with numbers makes it increasingly incomprehensible...but they are quicker to type.

Now you're expecting me to tell you the number assigned to "players-unit-type-count". After all, that's the last English word left, right? Nope, curveball. I do believe that each of the commands have their own internal command ID, but I haven't come across any documentation that lists the ID numbers for each command, and I'm not sure the AI engine would even allow you to use numbers instead of the command name. So, for purposes of this guide we'll leave the command name as is. However, there is one more part of our code that hasn't been converted to numbers yet, and that is the "==," the equals comparison sign. Yes, even the comparison symbols are assigned an ID in the AI engine. You can see the list of all comparison symbols here: link. From this list, you can tell that the AI engine will convert the "==" symbol to "4," making our code look like (players-unit-type-count -106 4 4 5). Woo numbers! Our conversion from AI code to numeric AI engine code is finally complete.

Now you might have two questions. First, you might wonder how the AI engine knows how to treat these numbers. For example, how does it know when 4 is an == sign, and how does it know when 4 is an archer? Internally, each command is built with an expected order of "parameters." For the players-unit-type-count command it expects the first parameter to be a player number, so it'll treat the -106 like a player number, which the AI engine knows is any-enemy. The next parameter it expects is a unit ID, so it knows to treat the first 4 as an archer, not an == sign. The third parameter it expects is a comparison operator, so it knows to treat the second 4 as a == sign, not an archer. The last parameter it expects is a value that it will use for comparison, so it will treat 5 like the numeric value five, rather than using the 5 as a unit ID or something like that. If you want to know what the AI engine expects for the parameters in each command, you can look up the command on the Commands page, click on the command to go to its details page, and look at the command's syntax to learn about each of its parameters. Here's the players-unit-type-count page.

The second question you might be having is how all of this stuff relates to defconsts, goals, or strategic numbers. Do we really need to go into all this detail? This foundation is important for our next topic, defconsts.

Index