Writing an AI script for Age of Empires III (Part I)

Here you can post about your scenario's, mods, custom maps and YouTube channels!
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Writing an AI script for Age of Empires III (Part I)

  • Quote

Post by AlistairJah »

Go to Part II
Go to Part III
Download the reference

INTRODUCTION

1. About this tutorial
This tutorial is a little project of mine, where I am trying to make those who read it capable of controlling the AI in Age of Empires III through an AI script. This tutorial aims to be as complete as possible, so that once you finish it, you have a knowledge that is solid enough to make you capable of writing an AI script completely from scratch, with your own style and method. I hope you will enjoy learning things from it as much as I enjoy writing it.

2. Requirements
• Age of Empires III - Complete Collection. The equivalent of it is the combination of all of the three Age of Empires III games: Age of Empires III, The War Chiefs and The Asian Dynasties.
• Basic computer knowledge, such as going to a certain folder, copying, moving, renaming, deleting files and folders and editing files with programs. Especially, you must know where is your copy of Age of Empires III installed.
• A text editor. I recommend a text editor that supports syntax highlighting (to know more, google it), like Notepad++ or Sublime Text. I personally prefer Notepad++, which can be downloaded here
• Patience. Among all of the things we can test/verify, AI-related things are probably the most time consuming.



CONVENTIONS

Before we continue, there are a few things we have to agree on to keep problems and misunderstanding away from us:

1. Backups
Please, oh please backup your files before editing them. Making a backup simply consists of copying the file and pasting the copy somewhere so you can restore it in case something goes wrong with the original.

2. Game Folder
When I say "Game Folder", it means the folder where Age of Empires III is installed.

3. User Folder
When I say "User Folder", it means the Age of Empires 3 folder in System Drive\Users\Your Username\Documents\My games



SETTING UP AN AI SCRIPTING PROJECT

Every AI scripting project begins with the following steps:

1. Enabling the developer mode
Note: doing these modifications will make you unable to play Multiplayer games unless the other players have the same files as you. Make sure you make backups of the original files so you can restore them when you want to play in Multiplayer mode again.

When the developer mode is enabled, we get an access to a whole set of features that are disabled by default. Here is how to enable the developer mode:

• open Game Folder\Startup\gamey.cfg with your text editor
• paste these lines at the bottom, in the very end:

Code: Select all



developer
aiDebug
logAIErrors
• save

The developer mode is now enabled.

But we also need some way to access the features that are unlocked. Here is how to do that:

• open Game Folder\Startup\gamey.con with your text editor
• paste these lines at the bottom, in the very end:

Code: Select all


map("alt-q", "root", "AIDebugInfoToggle()")
map("alt-e", "root", "configSet(\"showConsole\") console()")
map("esc", "console", "console()")
• save

You may have figured out what do these hotkeys do. We'll use these later.

2. Creating an AI script
When you are familiar enough with AI scripting, you can make your AI script the way you want. But, for now, I assume that you are a fresh rookie so I will show you the rookie way for creating an AI script.

• open your text editor
• create a new file
• write these lines:

Code: Select all


void main( void )
{

}
• save as Game Folder\AI3\AAI.xs

And that's it, you created an AI script!

> Seriously? That's all?
Yes, the file AAI.xs is an AI script. However, it is unused for now. Let's see how to use it!

3. Loading an AI script
For the purpose of this tutorial, we are going to test AI scripts mostly in scenarios. Actually, scenarios and the scenario editor offer the best tools for testing, not only AI scripts but also mods, triggers and even random maps in some cases.

• run The Asian Dynasties
• go to Help & Tools>Scenario Editor
• go to Scenario>Player Data
• in the section of Player 1, change Human to Computer
• click on the button AI
• find and select our AI script AAI.xs
• click on the button Load
• if you get an error or an empty dialog box, simply repeat the process from the 3rd step
• close the Player Data dialog
• save as AIScen.age3Yscn

4. Testing
• in the main menu, go to Single Player>Custom Scenario
• find and select the testing scenario AIScen
• click on the button Load

If there is no error, then congratulations! You did everything correctly and made an AI script!



THE BASICS OF XS, AN AOE3 SCRIPTING LANGUAGE

WHAT IS XS?

Nice-to-know but unnecessary information, XS stands for eXternal Subroutine. It is a language that allows us to tell the game to do something for us. In Age of Empires 3, XS is an extremely simplified version of C++ that is used for writing AI scripts, random map scripts, triggers, UI and certain commands used in modding.

When we want to tell the AI what to do, we write a series of codes (instructions or statements) in a text document and we save it as an .xs file. Then, we tell the game to load it and the game will play the role of the dude between the AI (the virtual player) and the AI Script (or... more like the parsed result). Therefore, we have to know how to "speak in XS" (lol) before we can communicate properly with the AI. A super cool bonus: once you're familiar with XS, you can also write your own random map scripts, create your own trigger conditions/effects or even use more advanced tools if you're willing to do more than AI scripting.

The long talk ends here, let's get to the point.


I. COMMENTING THE CODES

Since we are writing codes, i.e. something that is not in plain English, it is sometimes useful to explain what are these codes for, especially when you work with other people on the same script. We can do that by commenting, because comments are ignored by the game when it reads the script, which means we can write comments in plain English.

There are two types of comments:

1. Single-line comments
Single-line comments begin with a double slash //, and anything that is written after it (on the same line) is a comment:

Code: Select all


// This is a comment. We are going to write a code below:
float PI = 3.141592; // This is another comment: we are defining a variable named PI
2. Multi-line comments
Multi-line comments are written between /* and */:

Code: Select all


/* Welcome to my script!
I love writing scripts!
I also love commenting!
Goodbye! */

II. VARIABLES (PART I)

Variables are elements of XS for storing and manipulating the informations that will be needed throughout the execution of the script. For example, imagine a scenario with two teams: the government (played by you) and the rebels (played by the AI). We want the rebels to resign when they lose their Barracks three times. For that, we can define a variable number_destructions in the AI script for storing a number (initially 0) and adding +1 to it each time a Barracks of the AI is destroyed, and write a code to make the AI resign when the value of number_destructions reaches 3.

1. Defining a variable
Before we can use and manipulate a variable, we must define it first. The definition of a variable looks like this:

Code: Select all


type name = value;
type is a keyword that corresponds to the data type of the information we want the variable to store. See 2. Data types below.
name is the name of the variable. There are a few rules for naming:
- a variable name must be unique: several variables can't have the same name. In other words, we are not allowed to do more than one definition using a certain variable name. Note, though, that XS is case sensitive, which means, for example, that AA, aa, Aa and aA are all different.
- a variable can't have the same name as a function. See IV. FUNCTIONS further below.
- a variable name can only contain alphanumeric characters and underscore: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 _
- a variable name cannot start with a number: 1stName is INCORRECT. For example, use firstName.
= is known as assignment operator. It is for assigning the value on the right side of it to the variable on the left side of it.
value is the value (or information) to be stored by the variable. It must correspond to type.
; (semicolon) indicates the end of an instruction. The instruction here is a variable definition, but we'll discover more instructions later. Most instructions end with a semicolon, but there are a few exceptions that we will talk about, later.

2. Data types

Integers (int)
These are integer numbers. They can be negative like, for example, -273 or positive like 100. To simplify, 0 (zero) counts as positive.

According to the template we've seen in 1. Defining a variable, here are a few examples of the definition of an integer variable:

Code: Select all


int zeroKelvinCelcius = -273;
int zeroCelcius = 0;
int numberResourceTypes = 8; // Food, Wood, Gold, Fame, Trade, XP, Ships, SkillPoints
int numberResourceSubTypes = 7; // Easy, Herdable, Hunt, HuntAggressive, Farm, Fish, Trade
Floating point numbers (float)
These are numbers with a decimal part, like 3.141592. Like integers, they can be negative or positive. To simplify, 0.0 (zero) counts as positive.

According to the template we've seen in 1. Defining a variable, here are a few examples of float variable definition:

Code: Select all


float zeroKelvinCelciusForReal = -273.15;
float PI = 3.141592;
float treatyRadius = 80.0;
Strings (string)
These are... uhm, texts? I don't know what exactly is accurate for explaining strings but yes, we can say strings are texts:

Code: Select all


string myFavUnit = "The Tommynator";
string reason = "Because I suck";
Note the quotes. A string is delimited by quotes, and anything that is not inside the quotes is not part of the string.

Booleans (bool)
A boolean variable is a variable that can store only true or false.

Code: Select all


bool aBoolVariable = true;
bool anotherBoolVariable = false;
Vectors (vector)
Vector variables mainly store coordinates of the terrain, but their use isn't limited at that, though it is way too early to talk about vectors because I haven't even talked about functions yet.

It might be useful to know that a coordinate in Age of Empires III has 3 components:
• the X component corresponds to the X axis that starts from the extreme south to the extreme east
• the Z component corresponds to the Z axis that starts from the extreme south to the extreme west
• the Y component corresponds to the elevation

3. Updating a variable
Once a variable is defined, we can update it as much as we want:

Code: Select all


int hitpoints = 5;
hitpoints = 4;
hitpoints = 3;

float size = 10.0;
size = 5.0;
size = 2.5;
size = 1.25;
Obviously, the example above is completely pointless, it's just to show you that the values of variables can change.

4. Getting the value of a variable
We can also get the value of a variable (of course we can, the whole point of a variable is for storing and retrieving values into and from it). For example, we can get the value of A and store it to B:

Code: Select all


int A = 1;
int B = A;
5. Doing operations with variables
And we can do the following operations with variables:

• Addition +
• Subtraction -
• Multiplication *
• Division /
• Modulo % but it is rarely used. It's for calculating the remainder in a division, for example: the result of 3 % 2 is 1, which is useful for checking if a number is even or odd.
• Incrementation ++ which is the act of adding +1 to the value of a variable: i++ is exactly the same as i = i + 1
• Decrementation -- which is the act of adding -1 to the value of a variable: i-- is exactly the same as i = i - 1
• To negate the value of a variable, do it this way: a = -1 * a or a = 0 - a, because scripts Age of Empires III don't support the a = -a operation.

A few examples:

Code: Select all


int a = 0;
int b = 1;
int c = a + b; // The value of c is 1
a++; // It's like writing a = a + 1;
float d = 3.2 * 0.5;
d = d / 5.0;
c = 0 - c; // The value of c is now -1
Concatenation: a string-specific operation
You can put different strings together with the concatenation operator: +

Code: Select all


string firstName = "Alistair";
string lastName = "Jah";
string fullName = firstName+" "+lastName; // fullName is "Alistair Jah" now
/!\ Concatenation is NOT an addition, and a number written in a string is NOT a number

Code: Select all


string one = "1";
string two = "2";
string twelve = one+two; // The result is "12", not "3"!
Finally, this is not really an operator but for one or another reason, you might need to write a quote symbol in your strings. For that, you can use the escaping symbol \ (backslash):

Code: Select all


string hello = "I just wanted to say \"Hello\"... Hello!";
And if you want to write a backslash in your string, put a backslash before it:

Code: Select all


string hello = "I just wanted to write a backslash: \\";
Comparisons
We're getting back to booleans.

We can compare different values and when we do so, the result is a boolean value, i.e. true or false. There are 6 operators for comparisons:

• equal == (don't confuse it with the assignment operator!)
• not equal !=
• greater than >
• lesser than <
• greater than or equal >=
• lesser than or equal <=

We can store comparisons in boolean variables:

Code: Select all


bool b = ( 0 == 1 ); // false
b = ( 0 < 1 ); // true
Logic operations
We can also do logic operations with booleans, using the operators && (AND) and || (OR):

• true && true is true
• true && false is false
• false && true is false
• false && false is false

• true || true is true
• true || false is true
• false || true is true
• false || false is false

Code: Select all


bool a = ( 0 == 1 ); // false
bool b = ( 0 <= 1 ); // true
bool c = a && b; // false
c = a || b; // true
6. Order of operations
The order of operations rule applies to all operations (including the boolean ones) in an AOE3 script. Especially, with nested brackets like this one:
((a + b) * ( ( c + d ) / e ) + f ) * g
the innermost operations are executed first:
1. c + d (let's say it's w)
2. w / e (let's say it's x)
3. x + f (let's say it's y) and a + b (let's say it's z)
4. z * y * z

7. Constants
There are certain values that have a name and never change, like π (PI). You can store them in a special type of variable known as constant. To make a constant, add the keyword const when defining:

Code: Select all


// Syntax: const type name = value;
// Example:
const float PI = 3.141592;
const float treatyRadius = 80.0;
A constant can't be initialized with the value of a variable:

Code: Select all


// Something like this will instantly produce an error:
int thisOneIsNotConstant = 50;
const int thisOneIsConstant = thisOneIsNotConstant;
But can be initialized with the value of another constant:

Code: Select all


// This, however, is fine:
const int thisIsAConstant = -1;
const int thisIsAnotherConstant = thisIsAConstant;
A variable can be initialized with the value of a constant:

Code: Select all


// No further comment
const int cStrategyBoom = 0;
const int cStrategyRush = 1;
int gStrategy = cStrategyRush; // By default, we're rushing
8. Conversion
It is possible to store the value of a float variable in an int variable and vice versa. That is known as data conversion. A data conversion from a bigger data type to a smaller one leads to data loss:

Code: Select all


float aCertainNumber = 23.145;
int thatNumberAsInteger = aCertainNumber; // The value of this variable is 23, meaning that some part of information has been lost
The possible conversions are:
float to int

Code: Select all


float a = 23.145;
int b = a;
int to float

Code: Select all


int a = 10;
float b = a;
bool to int

Code: Select all


bool a = true;
int b = a;
bool to float

Code: Select all


bool a = true;
float b = a;
anything to string
We must concatenate though:

Code: Select all


bool a = true;
string b = ""+a;

III. CONTROL STRUCTURES

A script is not just a linear sequence of instructions. Sometimes we want certain instructions to be executed only if a certain condition is fulfilled. Sometimes we want to repeat certain instructions while a certain condition is fulfilled. For that, we use control structures. This section gives me the opportunity to introduce the concept of compound statement, also known simply as block. A block is simply a set of instructions that are put together between braces {} to be executed as one instruction.

1. Conditional structures: if and else statements
These structures look like this:

Code: Select all


if ( boolean )
{
instructions A
}
else
{
instructions B
}
If boolean is true, instructions A are executed, else they are ignored and instructions B are executed instead. The entire else{} block is optional:

Code: Select all


if ( boolean )
{
instructions A
}
There is also else if:

Code: Select all


if ( boolean )
{
instructions A
}
else if ( boolean )
{
instructions B
}
else
{
instructions C
}
Again, the entire else{} block in the end is optional.

The braces are not mandatory if there is only one instruction to execute:

Code: Select all


if ( boolean )
instruction; // Only one instruction
else if ( boolean )
instruction; // Only one instruction
else
instruction; // Only one instruction
2. Iteration structure: the while loop
A while loop is used for repeating a set of instructions while a certain condition is true. As soon as it's false, the loop is broken and the instructions aren't executed anymore.

Code: Select all


while( boolean )
{
instructions;
}
Example:

Code: Select all


int i = 0;
string message = "Counting from 0 to 9: ";
while( i < 10 )
{
message = message + i + ", ";
i++;
}
message = message + " done!";
• at first, the value of i is 0
• the while structure compares if it's lesser than 10
• it is, so the instruction block is executed
• after i++, the value of i is now 1
• the while structure compares again if it's lesser than 10
• it is, so the instruction block is executed
• and the process is repeated!
• at some point, the value of i is 10
• the while structure compares again if it's lesser than 10
• it isn't, so the loop is broken

By the end of the loop, the value of message is "Counting from 0 to 9: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, done!".

As long as boolean is true, the loop repeats, and as soon as it's false, the loop stops. So, please make sure you don't write an infinite loop because the game will freeze and sometimes, even the task manager won't help you.

3. Iteration structure: the for loop
The for loop is used for repeating a set of instructions a certain number of times. It looks like this:

Code: Select all

for( variableName = initialValue; comparisonOperator finalValue )
{
instructions
}
variableName = initialValue is, in fact, an int variable definition. Therefore, make sure that initialValue is an integer number or an integer variable.

As for comparisonOperator finalValue, in order to avoid an infinite loop, do as follows:
• if the value of variableName is less than that of finalValue, comparisonOperator should be < or <=
• if the value of variableName is greater than that of finalValue, comparisonOperator should be > or >=

• if comparisonOperator is <, then instructions will be executed finalValue - initialValue times and the value of variableName is automatically incremented in each iteration.
• if comparisonOperator is <=, then instructions will be executed finalValue - initialValue + 1 times and the value of variableName is automatically incremented in each iteration.
• if comparisonOperator is >, then instructions will be executed initialValue - finalValue times and the value of variableName is automatically decremented in each iteration.
• if comparisonOperator is >=, then instructions will be executed initialValue - finalValue + 1 times and the value of variableName is automatically decremented in each iteration.
• the value of variableName can be used in instructions, but can't be modified (i.e. you can't assign any value to variableName)

Jump statements: the break statement
As soon as it sees a break in a loop, the AI stops that loop even when the boolean for allowing the loop is true, and the rest of the instructions after break is ignored. Here's an example: we are going to stop a while loop before its "true" end:

Code: Select all

int i = 0;
while( i < 50 )
{
if ( i == 10 )
break;
i++;
}
Jump statements: the continue statement
As soon as it sees a continue in a loop, the AI ignores the rest of the loop in the current iteration and jumps directly to the start of the next iteration. As an example, we are going to "skip" the loop for i == 5:

Code: Select all

string message = "Counting from 0 to 9: ";
for( i = 0; < 10 )
{
if ( i == 5 )
{
message = message + " skipped, ";
continue;
}
message = message + i + ", ";
}
In the end, the value of message is "Counting from 0 to 9: 0, 1, 2, 3, 4, skipped, 6, 7, 8, 9"

4. The switch structure (also known as selective structure)
The basic principle of the switch structure is to take a value (valueToEvaluate), then check if it's equal to one of a few values (valueA, valueB, etc.), and in that case, execute the corresponding instructions:

Code: Select all

switch( valueToEvaluate )
{
case valueA:
{
instructions A
break;
}
case valueB:
{
instructions B
break;
}
case valueC:
{
instructions C
break;
}
// Add as many as necessary
default:
{
default instructions
break;
}
}
What happens?
1. the switch structure evaluates valueToEvaluate (which can be a variable or an operation like a + b etc.)
2. now, it checks whether or not it is equal to valueA
3. if it is, then instructions A are executed, and break; makes the AI exit this switch structure.
4. if it isn't, then valueB is checked if it's equal to valueToEvaluate
5. if it is, then instructions B are executed, etc.

If none of the values is equal to valueToEvaluate, then the default instructions are executed, though the entire default:{} block is optional.


IV. FUNCTIONS

A function has the following characteristics:
• a return type: can be void, bool, int, float, string or vector. When the function's return type is void, it doesn't return any value, else it returns a value of the same type as its return type (a bool returns a boolean value, etc.)
• a name: the rules for naming a function are the same as those of a variable:
- a function name must be unique: several functions can't have the same name. The case sensitiveness also applies.
- a function can't have the same name as a variable.
- a function name can only contain alphanumeric characters and underscore _.
- a function name cannot start with a number.
• zero, one or several parameters: parameters are similar to variables (have a type, a name and a value), are specific to the function in which they are defined and can be used in that function to change the way that function works.
• a group of statements: when the function is called, those statements are executed. Those statements can use the values of the function's parameters.

1. Defining a function
Before we can call a function, we must define it first. The definition if a function looks like this:

Code: Select all


returnType functionName( parameter1Type parameter1Name = parameter1DefaultValue/*, parameter2Type parameter2Name = parameter2DefaultValue, ...*/ )
{
statements
return( returnValue );
}
If the function is not supposed to return a value, returnType is void and return( returnValue ); is simply return;.

Also, if the function is not supposed to have parameters, we simply write void:

Code: Select all


returnType functionName( void )
{
statements
return( returnValue );
}
If there are several return statements, the function is terminated as soon as one of these statements is executed:

Code: Select all


returnType functionName( void )
{
statements
if ( boolean )
{
return( returnValue ); // If boolean is true, the function ends here after returning returnValue
}
return( returnValue ); // If the function didn't end at this point, it ends here after returning returnValue
}
2. Calling a function
Once a function is defined, it can be called. A function call usually looks like this:

Code: Select all


// Assuming that the variable has already been defined before:
variable = functionName( parameters );
The type of variable must be the same as the return type of the function.

If the function's return type is void, it can't be assigned to a variable, so we simply call it:

Code: Select all


functionName( parameters );
3. Function types
I don't know about the rest of the game, but Ensemble Studios provided 619 functions to be used for AI script development. Some of them simply don't work, while some others are working but aren't really useful, but these are not many.

All these 619 functions are already defined in the game (we say that they are native to the game), we don't need to defined them - we can't anyway, doing that will produce an error - we directly call them. You can find them all here. They are split in 3 groups: XS functions, AI functions and KB functions.

XS functions
There are 44 XS functions, which are all used for scripting purpose - they are all elements of the XS language the same way as variables and operators are elements of many programming languages.

AI functions
There are 285 AI functions, most of them are used for AI - gathering, building, researching, etc.

KB functions
There are 290 KB functions. KB stands for Knowledge Base. KB functions are used for getting informations on the current state of the game (number of units, current scores, amount of resources, etc.)

Don't be afraid by these numbers, those mean nothing! This tutorial will try to show most of the most used functions in AI scripting. Later on, you can always look at the reference if you're curious and want to discover more things.

4. The main function
Do you remember our AAI.xs script? We already defined a function in it! That function is the main function. It is mandatory in all Age of Empires III scripts. It is the very first function that is called when the script is executed. We don't need to write main() anywhere, the AI calls it for us. If we want to call other functions, we simply write the call statement in the main function or in another function that we call in main().

At this point, we've got enough knowledge for a relatively proper test. For that, you are going to need a function: aiChat. It is used for the AI to send a chat to a player:

Code: Select all


// Syntax: aiChat( int playerID, string message );
// Example:
aiChat( 1, "Hey, what's up?" ); // This sends "Hey, what's up" to player 1
Calling it in the main function:

Code: Select all


void main( void )
{
aiChat( 1, "Hey, what's up?" );
}
A bonus: the function aiRandInt that generates a random number:

Code: Select all


int randomNumber = aiRandInt( 10 ); // One of the numbers from 0 to 9
Here's the test: use all the knowledge you obtained so far to edit our AI script (AAI.xs) and make it different message to player 1 (i.e. itself, so you can see the message when testing).

Here's an example of mine, but please imagine your own fictive message and write your own script based on it!

Code: Select all


void main( void )
{
string food = "";
switch( aiRandInt( 5 ) ) // aiRandInt( 5 ) returns either 0, 1, 2, 3 or 4
{
case 0:
{
food = "french fries";
break;
}
case 1:
{
food = "hamburger";
break;
}
case 2:
{
food = "pizza";
break;
}
case 3:
{
food = "sandwich";
break;
}
case 4:
{
food = "taco";
break;
}
}

aiChat( 1, "Guys, I'm gonna eat some "+food+"!" );

string countdown = "In ";
for( i = 3; > 1 )
{
countdown = countdown + i + ", ";
}
countdown = countdown + " I'm out!";

aiChat( 1, countdown );

if ( aiRandInt( 2 ) == 1 ) // aiRandInt( 2 ) returns either 0 or 1
aiChat( 1, "I'm back!" );
}
5. User functions
For the second test, you must define your own functions and call them. See 1. Defining a function and 2. Calling a function and the previous test if you need help.

Here is an example of mine, you can copy and try it to get familiar with functions first, and make your own later.

Code: Select all


float min( float a = 0.0, float b = 0.0 )
{
if ( a < b )
return( a );
return( b );
}

float max( float a = 0.0, float b = 0.0 )
{
if ( a < b )
return( b );
return( a );
}

float abs( float a = 0.0 )
{
if ( a >= 0.0 )
return( a );
return( 0.0 - a );
}

void main( void )
{
int a = aiRandInt( 10 );
int b = aiRandInt( 10 );
aiChat( 1, "The bigger number is " + max( a, b ) + " while the smaller number is " + min( a, b ) );
int c = min( a, b ) - max( a, b );
aiChat( 1, "The actual value of c is " + c + " and its absolute value is " + abs( c ) );
}
V. VARIABLES (PART II)
Nice, we can now talk about the rest of what there is to talk about variables. This part is rather short and easy to understand.

1. Variable scope
There are two places where you can define variables:
• Inside a function:

Code: Select all

void main( void )
{
int variable = -1; // This variable has been defined inside a function
}
• Outside a function:

Code: Select all

int variable = -1; // This variable has been defined outside a function
void main( void )
{

}
A variable that is defined inside a function is a local variable. It is only accessible inside that function:

Code: Select all

void function( void )
{
int a = -1;
/* The variable a can only be accessed inside this function,
so it literally doesn't exist anywhere else*/
}

void anotherFunction( void )
{
int a = 0;
/* It particularly means that you can define a variable with
exactly the same name in another function, since these variables
are different (can contain different values)*/
}
Each time a function is called, all of the local variables in it are initialized with the values at their respective definitions.

A variable that is defined outside a function is a global variable. It is accessible anywhere after its definition (not before):

Code: Select all


/* The following variable is a global variable.
Any function can access it and modify its value,
but the name of it can't be used anymore when
defining a new variable or defining a function argument*/
int a = -1;

void function( void )
{
// Even here, you can't define a variable named "a"
a = 0; // But you can modify the value of the global variable "a"
}

void anotherFunction( void )
{
// In fact, any function can access the value of "a":
int b = a + 10;
// Or modify it:
a = 0;
// Obviously, the value will only change if the function is called somewhere
}
The actual value of a global variable is the last value assigned to it by a function.

2. Static variables
A static variable is a local variable that is initialized at the first call of the function associated with it and that keeps its value between the calls of that function.

In other words, when that function is called for the first time, the static variable is defined and initialized, and when the function is terminated, the variable keeps the last value that was assigned to it. The next time the function is called, the definition statement is skipped and the variable still contains the value that was assigned to it in the previous function call. See the difference by testing this:

Code: Select all

void function( void )
{
int aLocalVariable = 0;
static int aStaticVariable = 0;
aLocalVariable++;
aStaticVariable++;
aiChat(1, "The value of aLocalVariable is "+aLocalVariable);
aiChat(1, "The value of aStaticVariable is "+aStaticVariable);
}

void main( void )
{
function();
function();
function();
function();
function();
}
VI. ARRAYS
An array is a series of elements of the same type that are indexed. In other words, there are several elements in one array, and each element has its own unique index which is a number.

For example, five string elements can be defined in one array so you don't have to define five string variables, and you can access these strings later using the array and the proper index.

1. Defining an array
Like a variable and a function, you must define an array before you can use it.

An array has five characteristics:
• its type: an array of type X can only contain values of type X. There are five types: bool, int, float, string and vector
• its ID: when you define an array, it gets an ID number that permits you to distinguish it from other arrays. To make it simpler for you to work with that array, you can store its ID in an int variable.
• its size: an array of size Y can only contain Y elements.
• its default value: if no value is assigned to array in a certain index, it gets the default value.
• its description: apart from the ID, it's an additional way to make the difference between different arrays. The description must be unique, i.e. several arrays can't have the same description.

The definition of an arrays looks like this:

Code: Select all


int arrayID = xsArrayCreateType( size, defaultValue, description );
arrayID is the int variable that stores the ID of the array. Give it any name you want.
• if the array is a bool array, defaultValue must be a bool value, and the function is xsArrayCreateBool
• if the array is an int array, defaultValue must be an int value, and the function is xsArrayCreateInt
• if the array is a float array, defaultValue must be a float value, and the function is xsArrayCreateFloat
• if the array is a string array, defaultValue must be a string value, and the function is xsArrayCreateString
• if the array is a vector array, defaultValue must be a vector value, and the function is xsArrayCreateVector

2. Assigning values
Value assignment looks like this:

Code: Select all


xsArraySetType( arrayID, index, value );
arrayID is, well, the array's ID (the one that we get when we define the array)
index is the index of the element. It starts from 0 (zero), which means that the first element of the array is in index 0, the second element is in index 1, etc.
• if the array is a bool array, value must be a bool value, and the function is xsArraySetBool
• if the array is an int array, value must be an int value, and the function is xsArraySetInt
• if the array is a float array, value must be a float value, and the function is xsArraySetFloat
• if the array is a string array, value must be a string value, and the function is xsArraySetString
• if the array is a vector array, value must be a vector value, and the function is xsArraySetVector

3. Accessing values
Value retrieval looks like this:

Code: Select all


xsArrayGetType( arrayID, index );
You can eventually store the value in a variable:

Code: Select all


// Assuming that "variable" has already been defined before
variable = xsArrayGetType( arrayID, index );
Well, I guess you already got it now, right? I let you get the rest.

4. Getting an array's size
For that, you use the function xsArrayGetSize:

Code: Select all

xsArrayGetSize( arrayID )
5. An example before we finish
I was about to tell you to try to get it all on your own this time, by trying your own things as usual, but I ended up writing this small section anyway.

Code: Select all

void main( void )
{
int OttoDeck = xsArrayCreateString( 5, "Card", "Don't check the deck because this is just an array, not a deck builder" );
xsArraySetString( OttoDeck, 0, "HCShipSettlers3" );
xsArraySetString( OttoDeck, 1, "HCShipFoodCrates3" );
xsArraySetString( OttoDeck, 2, "HCShipFoodCrates2" );
xsArraySetString( OttoDeck, 3, "HCShipCoinCrates3" );
xsArraySetString( OttoDeck, 4, "HCShipJanissaries1" );

for( deckIndex = 0; < xsArrayGetSize( OttoDeck ) )
{
aiChat(1, "OttoDeck array, index "+deckIndex+": "+xsArrayGetString( OttoDeck, deckIndex ));
}
}
VII. RULES
Rules are void functions that are called periodically in a certain interval of time. Plus a few more details.

1. Defining a rule
A rule has the following characteristics:
• a name. Since a rule is a (void) function, the rules for naming a rule are the same as those of a function. Especially, a function and a rule can't have the same name.
• a state: active or inactive. An active rule is called periodically in a certain interval of time and can be deactivated. An inactive rule is like a mere void function. Be it active or inactive, a rule can be called like a void function.
• a launch method: runImmediately or not. When a rule has the runImmediately symbol, it's immediately called upon activation, and the timer for periodical calls starts. When it doesn't have the runImmediately symbol, the timer starts first and the rule is called only when the countdown reaches 0 (zero).
• a call interval: highFrequency or minInterval and/or maxInterval. highFrequency makes it that the rule is called extremely frequently, on the level of hundreds (maybe thousands!) or calls per second. minInterval and maxInterval makes it that the rule is called every X seconds (must be an integer constant number, can't be 0 or negative).
• a rule group. Several rules can be assigned to one group so they can be activated/deactivated together by simply activating/deactivating the group.

Code: Select all

rule ruleName
active // or inactive. You can't write both
runImmediately // omit this line if you don't want the rule to run immediately
highFrequency // omit this line if you want to assign a minInterval or maxInterval
minInterval X // X is an integer number greater than 0. Omit this line if you want highFrequency
maxInterval X // X is an integer number greater than 0 and greater than minInterval. Omit this line if you want highFrequency
// Note that if highFrequency, minInterval and maxInterval are all missing, highFrequency is the default
// Also, you can put minInterval without maxInterval and vice versa
group groupName // groupName is a string without quotes
{
instructions // I told you, a rule is a void function: you can write instructions here.
}
Let's test static variables again, with rules this time:

Code: Select all

rule aSimpleRuleToCallEvery5Seconds
active
minInterval 5
{
static int aStaticVariable = 0;
aStaticVariable++;
aiChat(1, "The value of aStaticVariable is "+aStaticVariable);
}

void main( void )
{

}
2. Activating and deactivating a rule
You can activate an inactive rule by calling the function xsEnableRule:

Code: Select all

rule aSimpleRuleToCallEvery5Seconds
inactive
minInterval 5
{
static int aStaticVariable = 0;
aStaticVariable++;
aiChat(1, "The value of aStaticVariable is "+aStaticVariable);
}

void main( void )
{
xsEnableRule("aSimpleRuleToCallEvery5Seconds");
}
You can deactivate an active rule by calling the function xsDisableRule or xsDisableSelf:

Code: Select all

rule aSimpleRuleToCallEvery5Seconds
active
minInterval 5
{
static int aStaticVariable = 0;
aStaticVariable++;
aiChat(1, "The value of aStaticVariable is "+aStaticVariable);
}

rule deactivatorThatWillAlsoDeactivateItself
active
minInterval 30
{
xsDisableRule("aSimpleRuleToCallEvery5Seconds");
xsDisableSelf();
}

void main( void )
{

}
As you can see (if you try these examples in an AI script, of course), an active rule becomes inactive when it is deactivated by xsDisableRule or xsDisableSelf, even if it has the active symbol, and an inactive rule becomes active when it is activated by xsEnableRule, even if it has the inactive symbol.

To try your own things, add the runImmediately symbol for example to see the difference between its presence and absence.

3. Rule groups
You can create a group by simply writing group groupName in a rule (groupName is the name of the rule group).

Code: Select all


rule rule1
inactive
minInterval 3
group timingRules
{
static int call = 0;
call++;
int time = xsGetTime()/1000;
aiChat(1, "This is the call number "+call+" of rule1 "+time+" seconds after the game has started.");
}

rule rule2
inactive
minInterval 5
group timingRules
{
static int call = 0;
call++;
int time = xsGetTime()/1000;
aiChat(1, "This is the call number "+call+" of rule2 "+time+" seconds after the game has started.");
}

rule rule3
inactive
minInterval 7
group timingRules
{
static int call = 0;
call++;
int time = xsGetTime()/1000;
aiChat(1, "This is the call number "+call+" of rule3 "+time+" seconds after the game has started.");
}

void main( void )
{
xsEnableRuleGroup("timingRules");
}
I don't have to explain it anymore, the example speaks by itself. When you try your own things, try to disable a rule group by using the function xsDisableRuleGroup.

VIII. Modular programming
When you are making a big big scripting project, it might be useful to split your big script file into several files of smaller size which serve different purposes. For example, you can have a file for all math functions, another file for gathering tasks, another file for attacking etc. You can then link them together using the include line:

Game Folder\AI3\math.xs

Code: Select all

float abs( float a = 0.0 )
{
if ( a >= 0.0 )
return( a );
return( 0.0 - a );
}
Game Folder\AI3\chat.xs

Code: Select all

void sendChatToAllies( string message = "" )
{
for( player = 0; < cNumberPlayers )
{
if ( kbIsPlayerAlly( player ) == true )
aiChat( player, message );
}
}
Game Folder\AI3\main.xs

Code: Select all

include "math.xs";
include "chat.xs";

void main( void )
{
sendChatToAllies( "Dear ally, greetings!" );
}
The path is rooted at Game Folder\AI3, so if a script is in a subfolder, just indicate the subfolder first by adding a slash, before writing the script's filename:
Game Folder\AI3\utility scripts\chat.xs

Code: Select all

void sendChatToAllies( string message = "" )
{
for( player = 0; < cNumberPlayers )
{
if ( kbIsPlayerAlly( player ) == true )
aiChat( player, message );
}
}
Game Folder\AI3\main.xs

Code: Select all

include "math.xs";
include "utility scripts/chat.xs";

void main( void )
{
sendChatToAllies( "Dear ally, greetings!" );
}
If you want your global variable to be useable across all files, add the keyword extern before the type:
extern const float PI = 3.141592;
extern bool isMapWater = false;



CONCLUSION
Aaand that's it for the first part! Though, later, I could rewrite a tiny bit if I deem it necessary. I hope you were attentive enough and practiced a lot, because understanding this first part is important for the rest of the tutorial!

Also... well, that's all, I think :D

See you in Part II!
User avatar
Germany richard
Dragoon
Posts: 341
Joined: Feb 24, 2015
Location: Germany

Re: Writing an AI script for Age of Empires III

Post by richard »

AlistairJah wrote:go to Game Folder\Startup and open god 1,y with your text editor (but make a backup first!)
Hi, thanks for your guide.

I did not find the file god 1,y

It is not in my Game Golder/Startup.
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Re: Writing an AI script for Age of Empires III

Post by AlistairJah »

richard wrote:Hi, thanks for your guide.
Hey, thanks for reading it :D
richard wrote:I did not find the file god 1,y

It is not in my Game Golder/Startup.
My bad. After a few re-read, I realized that there are a lot of typos. I fixed them :)
User avatar
Germany richard
Dragoon
Posts: 341
Joined: Feb 24, 2015
Location: Germany

Re: Writing an AI script for Age of Empires III

Post by richard »

Edited post. It worked now.
No Flag RefluxSemantic
Gendarme
Posts: 5996
Joined: Jun 4, 2019

Re: Writing an AI script for Age of Empires III

Post by RefluxSemantic »

So I read through some of this. The AI script is basically written in C isnt it?
User avatar
Germany richard
Dragoon
Posts: 341
Joined: Feb 24, 2015
Location: Germany

Re: Writing an AI script for Age of Empires III

Post by richard »

RefluxSemantic wrote:So I read through some of this. The AI script is basically written in C isnt it?
It is similar to C, but easier it seems. For example i see no pointer variables/ arrays.
No Flag RefluxSemantic
Gendarme
Posts: 5996
Joined: Jun 4, 2019

Re: Writing an AI script for Age of Empires III

Post by RefluxSemantic »

richard wrote:
RefluxSemantic wrote:So I read through some of this. The AI script is basically written in C isnt it?
It is similar to C, but easier it seems. For example i see no pointer variables/ arrays.
You can do C without using pointers though. And arrays are just lists of variables. I'd guess you can actually use arrays in your ai script.
User avatar
Czech Republic EAGLEMUT
ESOC Dev Team
Donator 05
Posts: 4515
Joined: Mar 31, 2015
ESO: EAGLEMUT
Clan: WPact

Re: Writing an AI script for Age of Empires III

  • Quote

Post by EAGLEMUT »

RefluxSemantic wrote:
richard wrote:
RefluxSemantic wrote:So I read through some of this. The AI script is basically written in C isnt it?
It is similar to C, but easier it seems. For example i see no pointer variables/ arrays.
You can do C without using pointers though. And arrays are just lists of variables. I'd guess you can actually use arrays in your ai script.
You actually can't use arrays like in C. The language is XS, which is a custom scripting language of AoE3 / Bang engine. Its syntax is very C-like, but there are some differences.
No Flag RefluxSemantic
Gendarme
Posts: 5996
Joined: Jun 4, 2019

Re: Writing an AI script for Age of Empires III

Post by RefluxSemantic »

I guess my coding experience is super biased towards numeric simulations, I struggle to understand how you can code things without arrays haha.

Anyways, this looks cool, might be a fun lockdown summer project.
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Re: Writing an AI script for Age of Empires III (Part I)

Post by AlistairJah »

This part is practically done, though I may update it from time to time for a better formatting and a better readability. Also, perhaps a summary.
Part II has begun: viewtopic.php?f=33&t=19970
User avatar
Great Britain I_HaRRiiSoN_I
Retired Contributor
Posts: 1626
Joined: Jan 15, 2016
Location: United Kingdom

Re: Writing an AI script for Age of Empires III (Part I)

  • Quote

Post by I_HaRRiiSoN_I »

can we add this to the esoc porn thread?
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Re: Writing an AI script for Age of Empires III (Part I)

Post by AlistairJah »

I added a section for the preprocessor command "include" and made a bit of reorganizing. I hope it's better than the last one, though I can reroll if necessary. Also changed the Startup instructions (User folder user.cfg and user.con instead of Game folder gamey.cfg and gamey.con) and moved rule from Part II to here. That's all (I think)

Ah, I also linked this to Part II, but that doesn't count :p
Sweden Zutazuta
Howdah
zuta
Posts: 1020
Joined: Apr 11, 2015

Re: Writing an AI script for Age of Empires III (Part I)

Post by Zutazuta »

This is awesome. Great job!
Pakistan inyourpc
Musketeer
Posts: 90
Joined: Jun 4, 2018
ESO: iamyawer
Location: in your base
GameRanger ID: 8789100

Re: Writing an AI script for Age of Empires III (Part I)

Post by inyourpc »

Yo. Can Ai play instead of human beings. Using Image proccessing. ?
User avatar
Belgium Circle of Ossus
Crossbow
Posts: 27
Joined: Feb 13, 2018
ESO: Lavilledieu

Re: Writing an AI script for Age of Empires III (Part I)

Post by Circle of Ossus »

This is such a great tutorial! Especially for someone like me, who only knows some basic Python, this is extremely helpful and in-depth.
Having a few questions:
- I assume you can assign the void type to a variable when defining it?
- Can I edit the AI while still ingame? Can I go back to the main menu, save the edits, and restart the scenario?
- If you use something like "ingameminutes" as variable in for ( ingameminutes, >Number ), how will the game handle that, since that variable should get decremented? In other words, how will the game handle decrementing variables that the game assigns itself?

I'm now at part 4. I'll continue reading later.
User avatar
Germany richard
Dragoon
Posts: 341
Joined: Feb 24, 2015
Location: Germany

Re: Writing an AI script for Age of Empires III (Part I)

  • Quote

Post by richard »

Circle of Ossus wrote: Can I edit the AI while still ingame? Can I go back to the main menu, save the edits, and restart the scenario?
Yes, you can edit and save your AI-script while the scenario is still running, then restart the scenario directly without even going back to the main menu.
No Flag spadel
Skirmisher
Posts: 131
Joined: Feb 24, 2015

Re: Writing an AI script for Age of Empires III (Part I)

Post by spadel »

I wonder if you could build a neural network to play Aoe3 on a competitive level. Personally, I believe no (at least not by self-play approach like in Go and Chess). But if you fix the map and the deck and maybe the matchup you could get pretty far I think.
User avatar
Czech Republic EAGLEMUT
ESOC Dev Team
Donator 05
Posts: 4515
Joined: Mar 31, 2015
ESO: EAGLEMUT
Clan: WPact

Re: Writing an AI script for Age of Empires III (Part I)

Post by EAGLEMUT »

Circle of Ossus wrote:- I assume you can assign the void type to a variable when defining it?
No, void type merely denotes the absence of any value returned from a given function. A variable value cannot be of "no value" type, that wouldn't really make sense.
Circle of Ossus wrote:- Can I edit the AI while still ingame? Can I go back to the main menu, save the edits, and restart the scenario?
Yeah, this should work fine, AI script is compiled when map loads.
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Re: Writing an AI script for Age of Empires III (Part I)

  • Quote

Post by AlistairJah »

Circle of Ossus wrote:- If you use something like "ingameminutes" as variable in for ( ingameminutes, >Number ), how will the game handle that, since that variable should get decremented?
Say you assign the value at the 5th minute of the game (the game time is in millisecond):

Code: Select all

for( time=(xsGetTime()/60000); > 0){}
The value of time is 5 here, and time is just a mere variable with that value, that value will not change with the game time unless you execute this statement again later on (for example one second later). It will just decrement as normal.
User avatar
Great Britain Panmaster
Skirmisher
Posts: 166
Joined: Jan 1, 2016

Re: Writing an AI script for Age of Empires III (Part I)

Post by Panmaster »

spadel wrote:I wonder if you could build a neural network to play Aoe3 on a competitive level. Personally, I believe no (at least not by self-play approach like in Go and Chess). But if you fix the map and the deck and maybe the matchup you could get pretty far I think.
It can be done using the UHC patch and +DontPauseWhenLosingFocus in user.cfg. You just need to write your neural network code in VS2019, then add it to the UHC project. Then add in-game xs functions to control the inputs and outputs and reset. Then patch the executable.Then write an AI script to test against and another AI script to be the neural network agent. Then create a scenario for the test with triggers to loop forever.
TAD AI Reference Guide
"Better to remain silent and be thought a fool than to speak out and remove all doubt." - Abraham Lincoln
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Re: Writing an AI script for Age of Empires III (Part I)

Post by AlistairJah »

I fixed some typos and rewrote certain parts after Swan's comments from Discord. Thanks Swan ^^
Australia Peachrocks
Lancer
Posts: 506
Joined: Jul 11, 2019
ESO: Peachrocks

Re: Writing an AI script for Age of Empires III (Part I)

Post by Peachrocks »

I would love to build AI and I think this stuff is easily transferred to DE where the AI has gotten even worse since last patch but yeahhh, that’s a whole lot of reading and things that are going over my head. Guess my mind set isn’t the best for this right now. I’d honestly pay someone to help out and do this for DE and make a solid project on trying to make solid AI. I play with it more than I care to admit...
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Re: Writing an AI script for Age of Empires III (Part I)

Post by AlistairJah »

Peachrocks wrote:I would love to build AI and I think this stuff is easily transferred to DE where the AI has gotten even worse since last patch but yeahhh, that’s a whole lot of reading and things that are going over my head. Guess my mind set isn’t the best for this right now. I’d honestly pay someone to help out and do this for DE and make a solid project on trying to make solid AI. I play with it more than I care to admit...
O ye, I feel you. The requirements for this are rather "costly": a lot of patience, a lot of free time, a lot of passion because this tends to get boring extremely quickly... not to mention the super-short lifetime of the Single Player mode for both multi and solo players and the lack of support from both modders and developers -- it's far better to focus on multi for many reasons.

But I still made this in case there are people who meet these requirements.

You can also do your modifications the "old" way: just copy-paste some codes here and there and make some tweaks until you feel like the AI has improved. A lot of modders improved their AIs that way, without any prior programming nor soft AI knowledge.
Australia Peachrocks
Lancer
Posts: 506
Joined: Jul 11, 2019
ESO: Peachrocks

Re: Writing an AI script for Age of Empires III (Part I)

Post by Peachrocks »

AlistairJah wrote:
Peachrocks wrote:I would love to build AI and I think this stuff is easily transferred to DE where the AI has gotten even worse since last patch but yeahhh, that’s a whole lot of reading and things that are going over my head. Guess my mind set isn’t the best for this right now. I’d honestly pay someone to help out and do this for DE and make a solid project on trying to make solid AI. I play with it more than I care to admit...
O ye, I feel you. The requirements for this are rather "costly": a lot of patience, a lot of free time, a lot of passion because this tends to get boring extremely quickly... not to mention the super-short lifetime of the Single Player mode for both multi and solo players and the lack of support from both modders and developers -- it's far better to focus on multi for many reasons.

But I still made this in case there are people who meet these requirements.

You can also do your modifications the "old" way: just copy-paste some codes here and there and make some tweaks until you feel like the AI has improved. A lot of modders improved their AIs that way, without any prior programming nor soft AI knowledge.
Still you put a lot of effort into this, so what about you? Have you built AIs?
Madagascar AlistairJah
Crossbow
Posts: 14
Joined: May 6, 2018
ESO: Aliwest
Location: Madagascar

Re: Writing an AI script for Age of Empires III (Part I)

Post by AlistairJah »

Peachrocks wrote:
AlistairJah wrote:
Peachrocks wrote:I would love to build AI and I think this stuff is easily transferred to DE where the AI has gotten even worse since last patch but yeahhh, that’s a whole lot of reading and things that are going over my head. Guess my mind set isn’t the best for this right now. I’d honestly pay someone to help out and do this for DE and make a solid project on trying to make solid AI. I play with it more than I care to admit...
O ye, I feel you. The requirements for this are rather "costly": a lot of patience, a lot of free time, a lot of passion because this tends to get boring extremely quickly... not to mention the super-short lifetime of the Single Player mode for both multi and solo players and the lack of support from both modders and developers -- it's far better to focus on multi for many reasons.

But I still made this in case there are people who meet these requirements.

You can also do your modifications the "old" way: just copy-paste some codes here and there and make some tweaks until you feel like the AI has improved. A lot of modders improved their AIs that way, without any prior programming nor soft AI knowledge.
Still you put a lot of effort into this, so what about you? Have you built AIs?
For certain mods, yes. I never had much interest into modifying the base game's AI script because, like, even if the AI has its weaknesses, it's already fully functional, you know? But it doesn't necessarily support all the things a mod implements. The most extreme example would be the Egyptians in Wars of Liberty.

At some point, I completely got rid of about 2/3 of Improvement Mod's AI script and rewrote those parts with my own method and approach. Later on, ageekhere re-re-reworked it and now the AI is much stronger and much more optimized. You should try it out.

Who is online

Users browsing this forum: No registered users and 6 guests

Which top 10 players do you wish to see listed?

All-time

Active last two weeks

Active last month

Supremacy

Treaty

Official

ESOC Patch

Treaty Patch

1v1 Elo

2v2 Elo

3v3 Elo

Power Rating

Which streams do you wish to see listed?

Twitch

Age of Empires III

Age of Empires IV