Source: baseAI.js

/**
 * @fileOverview Provides a skeleton implementation of an AI player.
 */

/**
 * @summary Numeric id of the player currently evaluated by the game
 *          engine.
 * @type Number
 */
var PlayerID = -1;

/**
 * @summary Enables output of debug information by a bot.
 * @type Boolean
 * @see API3.debug
 */
API3.DebugEnabled = false;

/**
 * @class
 * @summary Base class for computer players.
 * @description In 0AD, each computer player ("bot") is implemented as
 *              a set of JavaScript files, located within a separate
 *              directory below
 *              <code>/binaries/data/mods/public/simulation/ai</code>.
 *              A file named <code>data.json</code> controls loading
 *              the AI script code:
 *
 *              <pre>
 *{
 *  "name": "Tutorial AI",
 *  "description": "The Tutorial AI should only be used on the \"Introductory Tutorial\" scenario.",
 *  "constructor": "TutorialAI",
 *  "moduleName" : "TutorialAI",
 *  "hidden": true,
 *  "useShared": true
 * }
 *              </pre>
 *              In this file, the <code>"moduleName"</code> has to refer
 *              to a JavaScript namespace which holds an object
 *              constructor as named in the <code>"constructor"</code>
 *              property of the JSON file.This object is constructed
 *              when the AI enters a game. Usually, this constructor
 *              is a derivative of the <code>BaseAI</code> class (it
 *              is possible to operate without the BaseAI, but
 *              cumbersome as the engine data decoding had to be done
 *              by the implementor).
 *
 *              The <code>BaseAI</code> class does not perform any
 *              action on its own during a game, but provides the
 *              infrastructure to communicate with the Pyrogenesis
 *              engine in a more friendly way.
 * @param settings {Object} The game settings as passed by the
 *                          GameSetup code.
 */
API3.BaseAI = function(settings) {
	if (!settings)
		return;

	/**
	 * @summary Numeric player index of this AI instance.
	 * @type Number
	 */
	this.player = settings.player;

	/**
	 * @summary Turn counter.
	 * @description For AIs, it is advisable to not run through all of
	 *              its computations at each game turn to limit CPU
	 *              usage. A common method is to run the AI only at
	 *              each <em>n</em>-th turn, where <em>n</em> is the
	 *              number of players in the game. To distribute CPU
	 *              load evenly, usually player 1 will operate when
	 *              the turn counter is 1 (modulo number of players),
	 *              player 2 when turn counter is 2 (modulo...), and so
	 *              on.
	 * @type Number
	 */
	this.turn = 0;
};

/**
 * @summary Converts the AI runtime data into a simple structure.
 * @description For saving and restoring of games, the whole game
 *              state is <em>serialized</em>, i.e. stored in a
 *              permanent format. To allow continuation of the AI
 *              after a save/restore cycle, an AI player has to
 *              return an object here which allows to restore it to
 *              it's exact same state using the
 *              [Deserialize]{@link API3.BaseAI.Deserialize} function.
 *
 *              This function shall be overridden by derived classes.
 * @return {Object} An arbitrary but simple object (JSON-compatible)
 *                  which describes the current internal state of the
 *                  AI. As this object has to be stored in a file, no
 *                  complex elements (like classes, functions or
 *                  closures) shall be used.
 */
API3.BaseAI.prototype.Serialize = function()
{
	// TODO: ought to get the AI script subclass to serialize its own state
	// TODO: actually this is part of a larger reflection on wether AIs should or not.
	return {};
};

/**
 * @summary Restores the AI to a previous state.
 * @description This function shall be overridden by derived classes
 *              to allow saving and loading of games. See the
 *              description of
 *              [Serialize]{@link API3.BaseAI.Serialize} on how to use
 *              these functions.
 *
 *              The function will be called right after the object
 *              constructor when a game is loaded.
 * @param data {Object} An object returned by a previous serialization
 *                      of the AI.
 * @param sharedScript {API3.SharedScript} The new shared component to
 *                                         use.
 * @return undefined
 */
API3.BaseAI.prototype.Deserialize = function(data, sharedScript)
{
	// TODO: ought to get the AI script subclass to deserialize its own state
	// TODO: actually this is part of a larger reflection on wether AIs should or not.
	this.isDeserialized = true;
};

/**
 * @summary Initializes the AI.
 * @description When the AI object is constructed, some components of
 *              the game, such as the maps, are not yet available.
 *              Therefore, a second initialization step is provided
 *              where all of this informations can be used. Derived
 *              classes should use the
 *              [CustomInit]{@link API3.BaseAI.CustomInit} function
 *              instead to do their own initialization.
 * @return undefined
 * @private
 */
API3.BaseAI.prototype.Init = function(state, playerID, sharedAI)
{
	PlayerID = playerID;
	// define some references
	this.entities = sharedAI.entities;
	this.templates = sharedAI.templates;
	this.passabilityClasses = sharedAI.passabilityClasses;
	this.passabilityMap = sharedAI.passabilityMap;
	this.territoryMap = sharedAI.territoryMap;
	this.accessibility = sharedAI.accessibility;
	this.terrainAnalyzer = sharedAI.terrainAnalyzer;
	
	this.techModifications = sharedAI._techModifications[this.player];
	this.playerData = sharedAI.playersData[this.player];
	
	this.gameState = sharedAI.gameState[this.player];
	this.gameState.ai = this;
	this.sharedScript = sharedAI;
	
	this.timeElapsed = sharedAI.timeElapsed;

	this.circularMap = sharedAI.circularMap;

	this.gameType = sharedAI.gameType;

	this.barterPrices = sharedAI.barterPrices;

	this.CustomInit(this.gameState, this.sharedScript);
};

/**
 * @summary Allows for initialization of the AI.
 * @description During the constructor run of an AI, dynamic game data
 *              such as the territory and passability maps are not yet
 *              available. When initializations require such values,
 *              they may be done during the CustomInit step.
 * @return undefined
 */
API3.BaseAI.prototype.CustomInit = function() {
};

/**
 * @summary Handles the game engine update messages.
 * @description The game simulation is computed in "turns", where in
 *              each turn, the engine passes the differences to the
 *              previous turn to the AI players. At the very first
 *              turn, a complete description of the whole game is
 *              handed to the AIs.
 * @param state {Object} The delta values of the current game state to
 *                       the previous state.
 * @param sharedAI {API3.SharedScript} The shared data container to be
 *                                     used by the AI.
 * @todo Describe the intention of the playerID parameter.
 */
API3.BaseAI.prototype.HandleMessage = function(state, playerID, sharedAI)
{
	PlayerID = playerID;
	this.events = sharedAI.events;
	this.passabilityMap = sharedAI.passabilityMap;
	this.territoryMap = sharedAI.territoryMap;

	if (this.isDeserialized)
	{
		this.Init(state, playerID, sharedAI);
		this.isDeserialized = false;
	}
	this.OnUpdate(sharedAI);
};

/**
 * @summary Hook for derived classes to perform periodic updates.
 * @description This function is run once each game turn, after all of
 *              the delta values sent by the game engine have been
 *              applied to the representation seen by the AI (see
 *              [HandleMessage]{@link API3.BaseAI.HandleMessage} for a
 *              description on the update cycle).
 *
 *              For simpler AIs, overriding this function is the
 *              preferred method of running periodic tasks. Advanced
 *              AIs might override <code>HandleMessage</code> to
 *              analyze the engine messages directly for performance
 *              optimizations.
 * @return undefined
 */
API3.BaseAI.prototype.OnUpdate = function() {
};

/**
 * @summary Sends a message to all players in the current game.
 * @param message {String} The chat message to send.
 * @return undefined
 */
API3.BaseAI.prototype.chat = function(message) {
	Engine.PostCommand(PlayerID,{"type": "aichat", "message": message});
};

/**
 * @summary Sends a message to all player allied with the current
 *          player.
 * @param message {String} The chat message to send.
 * @return undefined
 */
API3.BaseAI.prototype.chatTeam = function(message)
{
	Engine.PostCommand(PlayerID,{"type": "aichat", "message": "/team " +message});
};

/**
 * @summary Sends a message to all enemies of the current player.
 * @param message {String} The chat message to send.
 * @return undefined
 */
API3.BaseAI.prototype.chatEnemies = function(message)
{
	Engine.PostCommand(PlayerID,{"type": "aichat", "message": "/enemy " +message});
};

// -------------------------------------------------------------------
//  End of file
// -------------------------------------------------------------------