== Current design == The AI design was reworked in r26274. The AI can now directly access simulation data via SimEngine.[wiki: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: {{{#!js 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: {{{#!js 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 {{{#!js 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.[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 }}}