A nice first reading is given in a 2012 interview with Jonathan Waller (quantumstate) on aigamedev: http://aigamedev.com/open/interview/ai-in-0ad/. The paragraph on the AI Architecture is still an up-to-date description of the AI interface.

AI scripts receive data from the engine about the current simulation state, once per simulation turn. This is a fairly low-level mechanism - it is expected that AI scripts will use a wrapper to provide more convenient access to the data. Currently this wrapper is implemented in common-ai/base.js.

The Engine uses the AIInterface and AIProxy components to provide this data. The AIInterface is a player level component which handles communication with the AI. AIProxy is an entity level component which creates a proxy representation of each entity to be given to the AI.

When the AI is initialised the constructor is called with one parameter:

settings = {
  "player": 1, // The player id.  Gaia is 0, players are number sequentially 1,...,n.
  "templates": ..., // An object which gives access to the template data from .../simulation/templates/

Every AI turn (currently happens every simulation update which is 5 times a second) the AI's HandleMessage method is called with one argument:

state = {
  "entities": ...,
  "events": ...,
  "players": [...],
  "timeElapsed": ..., // seconds since the start of the match
  "territoryMap": ..., // map of player territories
  "passabilityMap": ..., // Map showing where obstructions are
  "passabilityClasses": ...

entities

Each entity has a set of dynamic properties which are kept up to date. The AI can also access the template data for all entities.

An entity will have the form

var ret = {
  "id": 172, // This id is unique and permanent
  "template": "unit/hele_spearman",
  "position": [102.2, 34.7], // The unit position is undefined in some cases (e.g. garrisoned)
  "hitpoints": 80,
  "owner": 4, // Player who owns this unit
  "idle": true, 
  "unitAIState": "UNIT/INDIVIDUAL/GATHERING",
  "unitAIOrderData": cmpUnitAI.GetOrderData(),
  "trainingQueue": [{
                      "id": 7,
                      "unitTemplate": "spearman", // If this is a unit being trained
                      "technologyTemplate": "phase_city", // If this is a technology being researched
                      "count": 5, // number of units being trained
                      "progress": 0.78, // Proportion of training completed (range 0.0 to 1.0)
                      "metadata": {"role": "worker"}, // The AI can set metadata when adding a unit to the queue
                    }, ... ], // Array of items currently in the training queue
  "foundationProgress": 78, // Percentage complete for construction
  "resourceSupplyAmount": 195, // Current resources in a resource deposit (tree, mine, ...)
  "resourceCarrying": [{
                         "type": "wood", // Resource type
                         "amount": 8, // Amount currently being carried
                         "max": 20 // Maximum amout of this resource which can be carried
                       }, ... ], // array of resources being carried by a gathering unit
  "garrisoned": [167, 377, 345] // array of entity id's for the garrisoned entities
}

events

TODO

passabilityMap and passabilityClasses

state.passabilityMap = {
  "width": 256,
  "height": 256,
  "data": [ ... ] // Uint16Array with width*height entries
};
state.passabilityClasses = {
  "pathfinderObstruction": 1,
  "foundationObstruction": 2,
  "building-land": ..., // these are all the PassabilityClasses defined in simulation/data/pathfinder.xml
  ...
};

state.passabilityMap.data encodes all the passability data of each terrain tile. state.passabilityClasses gives bitmasks that define how to interpret state.map.data. For example:

// Get the bitmask for tiles that will obstruct foundations (i.e. you can't place any buildings there)
var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction");

for (var i = 0; i < map.data.length; ++i)
    if (map.data[i] & obstructionMask)
        ; // tile i is an unbuildable location

Since these are bitmasks, you can 'or' them together:

var obstructionMask = gameState.getPassabilityClassMask("foundationObstruction");

// Add in the bitmask for tiles that are obstructed for the "building-land" passability class
// (i.e. tiles that are underwater or too steep, based on the definition in pathfinder.xml)
obstructionMask |= gameState.getPassabilityClassMask("building-land");

for (var i = 0; i < map.data.length; ++i)
    if (map.data[i] & obstructionMask)
        ; // tile i is an unbuildable location for land-based buildings
Last modified 18 months ago Last modified on Feb 9, 2016, 8:19:58 PM