Strategy Guide

**Strategy Scripting: Beyond the Primer**

**Eric Dobbs, aka Void**

SAI Scripting Overview
Brief Introduction to Strategies
Conditions: Collecting Information on the Battlefield
Constant Conditions
Heterogeneous Conditions
Prerequisites: Organizing the Information
Example One: Putting It All Together
Note on Operators
Condition Strength
Prerequisite Weights
Strategic Object Modifiers

**INTRODUCTION**

The purpose of this paper is to document the SAI layer of Battlefield 2's AI and teach the reader how to write scripts that manipulate it. This little known and oft overlooked language produces dynamic gameplay effects if used properly. I will caution now that examples of SAI scripts found in the wild are usually trash; do not look to them as a teaching tool. I make exception for strategies written by FFOLKES.

You will find that in several places I have invented my own jargon to describe SAI coding – without official documentation this is inevitable. Any terms not defined in “A Primer on Writing Strategies for the AI of Battlefield1942”, by Tobias Karlsson, will be defined at first use. Access to that document is necessary to make sense of this paper, and I encourage you to at least skim through it before continuing. You may view the primer at any time by clicking the link below:

DICE Strategy Writing Primer

**SAI SCRIPTING OVERVIEW**

SAI scripts are composed of 3 main parts: Conditions, Prerequisites, and Strategies. In the big picture you can think of them thus:

**Conditions** – Collect information about the state of the battlefield

**Prerequisites** – Organize this information into logical sets / scenarios

**Strategies** – Act on the information

**BRIEF INTRODUCTION TO STRATEGIES**

A strategy manipulates the following:

**Aggression** - This is a floating point value ranging from 0 to 1.0. It does not control individual bot aggressiveness; it controls the aggressiveness of the whole team. To completely understand that, it is best to use different aggression levels in different strategies, but for now just be aware of this value's basic purpose.

**Number of Attacks** – This is an integer value that determines the maximum number of attacks available to the SAI. It is not obligated to launch as many attacks as this number specifies, but the SAI layer cannot launch more.

**Number of Defenses**- This is an integer value that functions similar to the above, with an important difference: It defines the minimum number of defenses the SAI may launch, though it may launch more. Note that this behavior is the opposite of Number of Attacks.

**SA Object Type Flag Modifiers**- These come in all shapes and sizes. They change the temperatures of Strategic Areas (SAs) according to flags that are set either in StrategicAreas,ai or set by the game. By flag I do not mean a control point – I am referring to tokens that provide the game with extra information about SAs.

**CONDITIONS: Collecting Information on the Battlefield**

There are three types of conditions: **Constant, Homogeneous**, and **Heterogeneous**. I will start by discussing constant conditions as they are the easiest to understand. Heterogeneous conditions will be treated later. There is nothing you can code with a homogeneous condition that you cannot code with a heterogeneous one; thus homogeneous conditions will not be discussed.

**Constant Conditions**

All conditions test two values using either subtraction or division.

* You may see in some scripts things like “Equal” or “EqualGreater” etc, but all comparisons work using subtraction or division, thus my use of symbols instead of text to denote the compare.

A constant condition compares one variable to a constant, that is, to a set number. Here is an example of a constant condition's first line:

aiStrategy.createConstantCondition oneEnemyFlag Crisp "-" Enemy ControlPoint 1

This condition asks if the enemy side has exactly one control point. Let's break it down:

aiStrategy.createConstantCondition oneEnemyFlag

Creates the condition and gives it a name.

**Crisp**

Makes the condition return either -1 (false) or +1 (true). There are other comparison types, such as “fuzzy”, but they are rarely useful.

**Enemy**

The variable will be taken from the enemy's database. The SAI maintains two separate databases for each side. In constant conditions the possible values are Enemy or Friendly.

**ControlPoint 1**

The variable, and the constant we are subtracting from it. If the enemy holds one Strategic Area that is flagged as a control point, then the result will be: 1 – 1 = 0. 0 is the default value that evaluates to True.

The target value that will make the condition true after the subtraction can be changed using this code line in the condition:

aiStrategy.targetValue [x]

Where x is a floating point value. For example, I could make the same check as the original condition with this code:

aiStrategy.createConstantCondition oneEnemyFlag Crisp "-" Enemy ControlPoint 2

aiStrategy.targetValue -1.0

Now the condition will evaluate to true if the subtraction results in -1. Observe that we are still checking if the enemy holds exactly one control point: 1 – 2 = -1

The reader may see little reason to use target values other than zero for a comparison, and for Constant conditions this is indeed true. Specifying a target value is much more useful in Heterogeneous conditions, particularly when the comparison uses division.

The last line of the condition specifies the condition's strength. I will explain the different condition strengths in detail later on. For now just observe the code line that completes the condition:

aiStrategy.setConditionStrength Required

**HETEROGENEOUS CONDITIONS**

A heterogeneous condition compares two variables to a specified target value. As with Constant conditions the default target value is zero, and you must specify from which side the variables are taken from. Unlike Constant conditions you may also specify Both for the database values. In that case the variable on the left is taken from the friendly side and the one on the right is taken from the enemy side.

Let's look at a few heterogeneous condition definitions. The following asks if the number of control points on the friendly side is greater than or equal to the number of neutral areas:

aiStrategy.createHeterogeneousCondition lotsOfNeutrals Crisp ">-" Friendly ControlPoint NumberOfNeutralAreas

aiStrategy.setConditonStrentgh Required

Now by changing the side to “Enemy”, we can check if the enemy has a number of control points greater than or equal to the number of neutral areas:

aiStrategy.createHeterogeneousCondition enemyHasLotsOfControlpoints Crisp ">-" Enemy ControlPoint NumberOfNeutralAreas

aiStrategy.setConditonStrentgh Required

Finally, a condition that checks if the friendly side has more control points than the enemy side using “Both” to specify the faction databases the variables are taken from. The target value must be set to 1.0 because I am asking if the enemy has more control points, not if it has greater or equal control points.

It may seem confusing at first, but the language for SAI scripting does not provide simple “<” or “>” operators. It doesn't really need to – you simply adjust the Target Value as shown to perform the required test.

aiStrategy.createHeterogeneousCondition friendlyHasMoreControlpoints Crisp ">-" Both ControlPoint ControlPoint

aiStrategy.TargetValue 1.0

aiStrategy.setConditonStrentgh Required

It should be evident by now that Heterogeneous conditions are more flexible than constant ones. However, in many scripts Constant conditions alone are sufficient to get the job done.

**PREREQUISITES Prerequisites: Organizing the Information**

The purpose of a prerequisite is to gather one or more conditions into a logical set and to use the organized information to trigger a strategy. Every strategy is triggered by exactly one prerequisite. A prerequisite attaches weights to conditions one by one, and these weights are summed together. The SAI is constantly evaluating prerequisites and chooses the one with the highest return value.

* The SAI is free to choose any strategy with a valid prerequisite, but almost always chooses the one with the highest value.

Here is an example of the code that creates a prerequisite:

aiStrategy.createPrerequisite someStrategyPrereq

aiStrategy.addCondition conditionOne 1.0

aiStrategy.addCondition conditionTwo 1.0

aiStrategy.addCondition conditionThree 1.0

When all these conditions are true, the prerequisite will return 3.0. If 3.0 is the greatest value out of all valid prerequisites, then the associated strategy will activate. The prerequisite for a strategy is assigned inside the strategy using this code:

aiStrategy.setPrerequisite [Prerequisite Name]

Though the name of a perquisite is arbitrary, it is highly recommended that you name it for the strategy it is associated with : ”[strategyName]Prereq” This makes your code easier to read and thus easier to debug.

The only thing left to do in order to tie all the parts together is assign your strategies to the teams you wrote them for using this code line:

ai.addSAIStrategy [x] [Strategy Name]

Where x is the team number (either 1 or 2) and “Strategy Name” is the name of the strategy you are assigning to the team.

**EXAMPLE ONE: Putting It All Together**

Look now at Example One:

Example\_One.ai

The goal of the example script 1 is to create a few simple strategies that cover the basics for a game round. There are two attack strategies and one for defense. The script was made using constant conditions only, with all conditions having strength “Required”.

**NOTE ON OPERATORS**

There are several ways to specify the comparison operation. You can use text, or you can use symbols. This document uses symbols from the subtraction and division families as these are closer to the way the comparison is performed. Note that all operators work the same way. For example, “>-” is the same as “>=”.

The operators used in this document and in my example scripts stand for the following:

“>-” difference is greater than or equal to the target value

“<-” difference is lesser than or equal to the target value

“-” difference is equal to the target value

“>/” quotient is greater than or equal to the target value

“</” quotient is lesser than or equal to the target value

“/” quotient is equal to the target value

**CONDITION STRENGTH**

A **Required** condition must be satisfied in order for the prerequisite containing it to return “true”. If any required condition in a prerequisite is false the prerequisite is invalid.

**Advisory** conditions, by contrast, sum their value to the prerequisite even if that value is negative. Since a prerequisite can have a negative return value thanks to advisory conditions, prerequisites are not boolean.

* Prerequisites that return exactly zero are considered “Undecided”, but the SAI will only pick those strategies if there is no valid ( > 0 ) prerequisite.

Advisory conditions serve to raise or lower the probability that a given prerequisite will be chosen. More details on Advisory conditions in the next script example.

**PREREQUISITE WEIGHTS**

In the example, all prerequisites have a return weight of 5.0 if they are valid. The defense strategy, “defendTheMap” contains two conditions, each of which is weighted 2.5. Since these are required conditions the prerequisite will only be valid if both conditions are true. If that is the case the prerequisite will return 2.5 + 2.5 = 5.0.

The condition named “enemyHasAtLeastOneControlPoint” can be true at the same time as the condition “atLeastOneNeutral”. In this case the SAI is equally likely to pick either strategy since both prerequisites assign these conditions a weight of 5.0.

**STRATEGIC OBJECT MODIFIERS**

Strategic object modifiers (temperature multipliers) are flags that give the game extra information about strategic areas. Many are static and set by hand in the file StrategicAreas.ai, others are dynamic and set by the game. In addition, multiple flags can be specified with a separate status.

In the example script I only use the Strategic object modifiers for ControlPoint,in several flavors. The options are Neutral, Hostile, and Owned. In the attack strategies I use neutral and hostile; in the defense strategy I specify owned. “ControlPoint” is one of the SA flags set in StrategicAreas.ai.

Going back to Example One, observe the object modifiers:

aiStrategy.setStrategicObjectsModifier ControlPoint 10.0 Neutral

aiStrategy.setStrategicObjectsModifier ControlPoint 20.0 Hostile