While I have been advocating the use of reactive planning for over a year now, there is often a large amount of middleware between a game environment and the reactive planning agent that needs to be defined in order to make use of ABL in games. The goal of this article is to provide a tutorial for interfacing a game environment with a simple ABL agent.
One of the questions brought up after my talk at the Paris Game AI Conference was whether ABL is available for distribution and if there is documentation available. Currently, the ABL binaries are available for non-commercial use, but the source does not have a specific license assigned. There is documentation available for ABL, but it is spread across several sources. For an overview of the language and semantics, the ABL Wiki is a good starting point. For additional discussion on the usage of the language to author agents, there is Michael Mateas’ dissertation which includes a chapter on ABL, and shorter papers on authoring idioms in ABL for Façade and EISBot. ABL has also been discussed previously on the forums at AIGameDev.
The source code and ABL binaries for the tutorial are available here. The Zip file contains source for the game, ABL agent, and ABL interface as well as binaries for compiling and running the ABL agent. The goal of the tutorial is to provide a micro-domain for demonstrating the ABL interface for authoring game AI. The “game” is called ABL Chaser and involves movement and bullet shooting interactions. For this tutorial, I am creating an ABL agent that moves towards the player while simultaneously firing bullets at the player. ABL is used to control the Chaser NPC (red square), while the player controls the blue square. Features such as collision and score have been omitted to reduce the amount of code needed.
ABL is a reactive planning programming language. Authoring game AI in ABL consists of defining sensors and actuators for the agent to interact with, as well as declarative behaviors which specify how the agent achieves goals. The ABL binaries consist of two parts: a compiler and a runtime component. The ABL compiler is a Java library that translates ABL code into Java code. An ABL agent which has been compiled to Java is referred to as a Behaving Entity. The ABL runtime component is a Java library for executing a Behaving Entity (ABL agent).
I used the Eclipse IDE to build this project, but it is not necessary to run the tutorial. The figure above shows the project structure, which includes a source directory for the game and ABL agent, as well as a lib directory which includes the ABL jar. The project also includes the higher-order java library (hoj.jar), which is necessary only during ABL agent compilation and not required by the ABL runtime component.
Compiling the ABL agent and game is a two step process. First, it is necessary to translate the agent code from ABL to Java. To perform this task, I have included a Compile class which is a simple script for compilation. The output of this process is a collection of generated Java files, which are placed in src/abl/generated. Second, it is necessary to compile all of the files, which include the game, interface, and generated code. Eclipse will handle most of the second step for you, but it is necessary to refresh the generated code directory after each ABL compilation to notify the IDE that a code update has occurred. Once everything has been compiled, the Chaser class can be used to run the game.
An ABL agent interacts with a game world through the use of sensors and actuators. Sensors are used to perceive world state and store facts about the game world to the agent’s working memory, while actuators (primitive acts) are used to perform actions in the game.
The class Chaser provides several methods for interacting with the game. The following methods provide functionality for sensing game state and are used by the sensors:
- public Point getPlayerLocation();
- public Point getPlayerTrajectory();
- public Point getChaserLocation();
- public Point getChaserTrajectory();
To perform actions in the game, the Chaser class includes the following methods:
- public void setChaserTrajectory(Point trajectory);
- public void fireChaserBullet(Point source, Point target);
The middleware between the game and ABL agent includes defining working memory elements (WMEs) which are facts about the world, sensors for populating working memory with WMEs, and actuators for performing actions. The middleware provides an interface between the declarative behaviors specified in ABL code and the game.
There are several additional concerns in the middleware that are not covered in this tutorial. First, scheduling actions for execution is necessary for a reactive planning agent. In this domain we assume actions are performed instantaneously to reduce complexity. Second, the game has been simplified to minimize synchronization issues that can occur due to the ABL decision cycle thread running asynchronous to the game update logic.
Working Memory Elements (WMEs)
The base data structure in an ABL agent’s working memory is the Working Memory Element (WME). A WME provides a set of properties for describing an instance in the game world. The tutorial includes WMEs for tracking the player and the chaser objects. It is also possible to include additional game objects in working memory such as the bullets, but the current version of the agent does not incorporate bullets in its decision making process.
The src/abl/wmes directory contains the code for defining working memory elements. The two classes each extend the WME base class which provides functionality for storing data in the agent’s working memory. The PlayerWME has properties for describing the player character’s location and trajectory. Properties can be exposed to the ABL agent by defining Java Bean style getters.
ABL sensors are used to perceive game state. In this tutorial there is a sensor for each type of WME that is monitored by the agent. The sensor code is located in the src/abl/sensors directory. Each time the sense method is invoked, the PlayerSensor creates a new PlayerWME which records the location and trajectory of the player character. While this example shows how to update working memory by creating new instantiations of WMEs, it is also possible to update the properties of an existing WME in the sense method. Working memory is accessed via static reference to the Behaving Entity. ThreadContext objects are used by the ABL runtime to enable multiple ABL agents to run within a single Java process.
The actions the agent can perform are defined by actuators in the src/abl/actions directory. The project includes actions for firing, moving, and stopping the agent. Each action implements an execute method which invokes a method on the Chaser class.
An action has a set of input arguments, which are parameters that must be provided to the action when invoked by the ABL agent. The prototype of a specific action is defined in ABL Code.
In this example, the agent assumes actions complete instantaneously. The base action class in this example marks actions as completed as soon as the agent queries the completion status, which occurs at the beginning of each decision cycle. It is also possible to author durative actions by manually setting the completion status once a scheduler has completed executing the action.
The behaviors driving the agent’s goal pursing behavior are defined in ChaserAgent.abl. There are several parts to the file, which include package definitions, import directories, sensors used the agent, actions available for execution, and behaviors. Additional documentation about each part of the agent is provided in the ABL source file.
An overview of the agent’s active behavior tree during execution is shown in the figure above. The figure shows behaviors that have been selected for execution in pursuit of goals. The agent contains two main goals, which are to move towards the player and to occasionally fire bullets at the player.
Specificities and priorities are used to determine which behaviors to examine for execution. Priorities are used as a step modifier, and the step with the highest priority is selected for execution each decision cycle. Specificities on the other hand are specific to a particular goal, and when the agent retrieves behaviors for pursuing a specific goal, the behavior with the highest specificity and true activation conditions is selected for expansion. Vertical movement behaviors have a higher specificity than horizontal movement, which is why the chaser moves vertically first if necessary.
The ABL runtime applies a heuristic that prefers to evaluate the current line of expansion, which actually causes behavior thrashing in this simplified example. To ensure that the agent both fires and moves towards the player, a higher priority is assigned to firing, which includes a cooldown period.
This tutorial has provided an overview of the components and code necessary to interface an ABL agent with a game. More detailed comments are also provided in the source code. The complete system in action is shown in this video. While the example does not demonstrate the most complex behavior for this domain, it lays groundwork for incorporating complex decision making systems in game AI.
About the author: Ben Weber is a PhD student at UC Santa Cruz.