Current design

The AI design was reworked in r26274. The AI can now directly access simulation data via SimEngine.QueryInterface(), as the regular components can.

The legacy AIInterface data still exists, though the idea is to reduce its scope over time.

Legacy Design

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:

  "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:

  "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

  "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.[wiki: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` ==
{{{#!js state.passabilityMap = {

  "width": 256, "height": 256, "data": [ ... ] // Uint16Array with width*height entries

}; }}} {{{#!js 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:

{{{#!js // 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:

{{{#!js 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 2 years ago Last modified on Jan 30, 2022, 2:39:29 PM
Note: See TracWiki for help on using the wiki.