Classic Battlefield Modding Wikia
Advertisement

Strategy Scripting: Beyond the Primer

Eric Dobbs, aka Void

Table of Contents
Introduction

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

Advertisement