Wesnoth AI Framework
Contents
Wesnoth AI Framework: A Composite AI
The Wesnoth AI framework is that of a composite AI. In other words, rather than the AI being one huge monolithic block, it is composed of a variety of different components which can be combined in a modular fashion (almost) at will.
In its default configuration, Wesnoth provides a predefined combination of these components in the RCA AI. This configuration can be modified in a multitude of different ways. Due to the modular setup, it is not only possible to add new AI behavior to before or after the default AI moves, or replace the default AI entirely, but also to insert new behavior routines in between existing components. This can be done either in addition to or as substitutions for existing components.
This page describes the AI components and their configuration in a general sense. Check out RCA AI for a description of how these are assembled into the default AI and Wesnoth AI for a collection of links to pages explaining how to customize and modify the existing AI or write a completely new AI.
Types of AI Components
The Wesnoth AI framework consists of the following types of components:
Stages
These are the fundamental building blocks of the AI. The AI is set up as a series of stages which are executed in the order in which they are put into the AI configuration. In other words, all actions of the first stage are executed first, then all actions of the second stage, etc.
While there are six different types of stages, only one of them, called the main_loop stage, is used in the default AI. In fact, this stage is sufficient for the vast majority of custom AI applications as well. We therefore only describe the main_loop stage and its sub-components in detail.
The Candidate Actions of the main_loop Stage
The main_loop stage consists of a number of candidate actions (CAs), each of which contains an evaluation and an execution function. For each AI move, the evaluation functions of all CAs are called, with each of them returning a score. The execution function of the CA with the highest score is then called, which results in an AI move (or in some cases, several moves) to be done. Then all CAs are evaluated again (including the one that was just executed) and the highest scoring CA is executed, until no CA returns a valid (non-zero) score any more.
Combining candidate actions designed for specific tasks such as attacks or village grabbing, it is possible to create entire AIs in a modular fashion. The CAs can be put together in whatever order and combination best suits the needs of a scenario. They do not need to be sorted in order of their evaluation scores and can even return variable scores which adapt to the situation on the map.
For a summary of the candidate actions of the default AI, click here.
Aspects and Goals
Aspects are configurable input parameters used to customize a candidate action's behavior. They are implemented as the the return values of functions that are called by the CAs of the RCA AI. They can also be called and taken into account by custom CAs, but that is not necessarily always the case. For example, some of the Micro AIs respect some of the RCA AI aspects (such as avoid), but not all do.
The return values of the aspect functions can be defined and/or customized using both WML or Lua.
Goals are used similarly to aspects in that they also affect the behavior of the default AI. They can be used to customize the directions into which the default AI moves its units. However, both their syntax and their internal implementation (see below) are different from those of the aspects.
Check out the RCA AI page for details on which aspects and goals exist and how they are used by the default AI.
Engines
Engines assemble the configurations provided for the individual components of the AI into usable code and execute this code. In recent Wesnoth versions, you do not need to worry about the engines any more, they are always defined and active by default. It is sufficient to know that the components can be written in three different languages, C++, Lua and Formula AI (FAI). Information on how to do this is provided in the links of the Wesnoth AI page.
It is worth noting that some stages are only set up for specific engines (see below), while the candidate actions, aspects and goals of the main_loop stage can be written in any of the three languages and can be combined arbitrarily.
AI Configuration
The Wesnoth AIs are assembled from configurations provided in several different places:
- The default AI parameters in a variety of configuration files in the 'data/ai/' directory
- Optional [ai] or [modify_ai] tags in era configurations
- Optional [ai] or [modify_ai] tags in multiplayer faction configurations
- Optional [ai] or [modify_ai] tags in scenario configurations
When the game is created, all these config snippets are merged into a single configuration stored in form of an [ai] tag. Then, aspects with the same ids are merged. During the game, whenever a [modify_ai] tag is acted upon (for example in an event), it is added into the existing AI configuration in the same way.
In the following, we describe the syntax of the [ai] tag. We first show the top-level keys and tags that can be used. Details on the use of the tags for the individual components follow after that, together with examples and links to more information for each component.
The [ai] Tag — Top-level Elements
AI configurations are set up and stored using the [ai] tag. It has the following top-level elements, which are described in more detail in the remaining sections of this page.
- description="": (translatable string) This is the text displayed in the MP setup menu when choosing an AI for a computer player.
- id="": (string) The id of the [ai] config snippet. This was originally meant to make it possible to remove [ai] configurations selectively during a game. However, due to the merging process described above, this information is lost. The id is therefore meaningless in the current implementation of the AI.
- version="": (string) This key is currently still needed to distinguish whether aspects are defined using standard or composite syntax in the [ai] tag. If it is omitted, standard syntax is used. If set to '10703' or '10710', the engine uses composite syntax. However, we are in the process of removing this disinction. When that is done, both types of syntax can be used without needing the version key, even mixed together inside the same [ai] tag. We will also deprecate this key when that happens.
- [stage]: Configuration of the stage(s) used in the AI. Several [stage] tags can be used, even several stages of the same type. They are executed in the order in which they are put into the [ai] tag.
- [aspect]: Configuration of the aspects to be used by the AI. These are in composite aspect format.
- Most aspects can also be provided using the simpler syntax explained at AiWML.
- [goal]: Configuration of the goals to be used by the AI.
- [engine]: This tag used to be needed for the Lua AI engine. It is not required any more, the Lua engine is created automatically now, just as the two other engines. If you need information on how this used to work, check out the Lua AI legacy documentation.
- formula="": (string) Formula AI code to be executed by the AI. We recommend not using this any more. See the Formula AI legacy documentation for more information.
The [ai] Tag — Stages
The [ai] tag can contain one or several stages defined in [stage] tags. If the AI configuration contains several stages, they are executed in the order in which they are put into the [ai] tag.
While there are six different types of stages available, all AI functionality possible in Wesnoth can be achieved using only a single one, the candidate action evaluation loop stage. In the default setup, this stage has id 'main_loop' and we generally simply refer to it as the main_loop stage.
Other stages exist mostly for legacy reasons from the times in the AI development when the main_loop stage did not exist yet. They are currently still functional and kept for backward compatibility reasons, but they are not maintained any more and might be removed in a future Wesnoth release cycle. We therefore recommend that all custom AI development is done using the main_loop stage only.
main_loop Stage Configuration
For the main_loop stage, the [ai] tag contains the following elements:
- name: This must be set to 'testing_ai_default::candidate_action_evaluation_loop', so that the AI knows that this is the main_loop stage.
- id: (string) This is the identifier of the AI. It is usually set to 'main_loop', but can take on any value. It only matters if you are planning to remove or modify the stage during a scenario.
- engine=cpp: (string) This must be set to 'cpp', which is also the default value. It can therefore be omitted.
- [candidate_action]: Candidate action configuration tags. See below for details.
Notes:
- As an example, the configuration of the main_loop stage of the default AI is shown here.
- Candidate actions can use any of the three available engines. Just because the stage uses the cpp engine does not mean that the CAs need to do so as well. See below for an example.
- While that should rarely ever be necessary, it is possible to include several main_loop stage definitions in the same [ai] tag.
- This is done by putting several [stage] tags into the same [ai] tag
- The AI engine then evaluates the CAs of the first stage in the usual way, until no CA returns a valid score any more.
- After the first stage is finished, it moves on to the second stage and repeats the process, until all stages have finished. This ends the AI turn.
- Obviously, this only makes sense if the sets of CAs of the different stages are not identical (although individual CAs might appear in several stages).
Recruitment Stage
There is a specialized configurable recruitment stage. However, this stage uses the recruitment functions of the old Default AI (the one used before the RCA AI existed). Anything that can be done with this stage, and more, can now also be done with the recruitment candidate action using the recruitment_instruction aspect. It should therefore never be necessary to use this stage and we do not describe it here.
Legacy Stages
Four other stages are also available. They do, however, not provide any functionality that is not also possible with the main_loop stage, only different ways of achieving the same functionality. We therefore only list them here for completeness, with brief examples and links to legacy documentation.
Note that both this documentation and the stages themselves might be removed at some point.
Lua stage
Executes Lua code by calling a function created inside a Lua engine. Whatever element is returned by the code in the Lua [engine] tag is accessible here as an upvalue.
So, for example, (...):execute()
calls whatever_is_returned_by_lua_engine:execute()
.
[stage] engine="lua" code= "(...):execute()" [/stage]
See the Lua AI legacy documentation.
Formula AI Side Formulas Stage
This stage executes a given Formula AI side formula. Example:
[stage] engine=fai name=side_formulas move="write_your_formula_here" [/stage]
See the Formula AI legacy documentation.
Formula AI Unit Formulas Stage
This stage takes no parameters and executes all Formula AI formulas which are attached to units. Example:
[stage] engine=fai name=unit_formulas [/stage]
See the Formula AI legacy documentation.
Fallback stage
Fall back to another AI. Example:
[stage] id=fallback name=testing_ai_default::fallback [ai] ai_algorithm=default_ai [/ai] [/stage]
This example shows how to set up a stage to fall back to the old Default AI. This was useful when it was not clear yet how well the RCA AI would work and whether it would suddenly stop in the middle of the CA evaluation loop. There is really no reason to use a fallback stage any more these days, especially since it is also possible to simply add another stage to the AI configuration (without that having to be a fallback stage).
The [ai] Tag — Candidate Actions
The main_loop stage (and only this stage) can contain an assortment of candidate actions. They are evaluated and executed in the manner explained above. This section explains how to configure the [candidate_action] tag that goes into the [stage] tag of the main_loop stage.
All three engines can, in principle, be used to write custom CAs. However, only the use of Lua CAs is recommended for this purpose. Using the cpp engine has the obvious problem of requiring changes to the Wesnoth source code. Formula AI CAs are discouraged for the same reasons why we do not recommend using Formula AI stages any more.
The [candidate_action] Tag
The [candidate_action] tag can contains the following keys:
- engine: This key is required for Wesnoth to know which engine the CA should be processed with. Possible values are 'cpp', 'lua' and 'fai'.
- id: The id of the CA is, in principle, optional. However, it is needed if you want to remove or change the CA after its initial definition.
- name: The CAs of the default AI are identified by their name, making this a required key for the cpp engine. Check here for the names of the available default CAs. The key is optional for other engines.
- max_score: The maximum score the CA evaluation function might return. This parameter is optional, but it is useful to reduce the AI move evaluation time. During each iteration through the evaluation loop, the main_loop stage only evaluates CAs with max_score larger than that of the highest-scoring CA found so far during this iteration. For that same reason, it also makes sense to order CAs by decreasing max_score in the [stage] tag, even though that is not technically necessary.
- score: (cpp engine only) The score returned by the CA. Note that this means that cpp engine CAs always have fixed scores.
In addition, Lua CAs also contain the following keys:
- location: File name, including path, of the AI code for the CA.
- eval_parms, exec_parms: The parameters to be passed to the evaluation and execution functions of the AI. These need to be in form of a Lua table, without the outer curly brackets. They are passed to these functions as the second argument, cfg.
Notes:
- There is also an old syntax for defining Lua CAs. However, this method requires the definition of a Lua engine using the [engine] tag. This is much less convenient than the current method without any advantages and will therefore be deprecated at some point in the future.
- If you need information on the old syntax, check out the Lua AI legacy documentation.
- The current syntax was originally called external Lua CAs. However, as this has now become the standard, we drop the external and simply call them Lua CAs.
- Formula AI CAs contain some additional keys. As we do not recommend using the fai engine any more, we do not describe this syntax here and refer to the Formula AI legacy documentation instead.
Here are examples of [candidate_action] tag uses:
Combat CA of the default AI:
[candidate_action] id=combat engine=cpp name=ai_default_rca::combat_phase max_score=100000 score=100000 [/candidate_action]
Lua AI CA:
[candidate_action] engine=lua name=return_guardian_bob id=return_guardian_bob max_score=100010 location="~add-ons/my_addon/lua/return_guardian.lua" eval_parms="id = 'Bob', return_x = 10, return_y = 15" exec_parms="id = 'Bob', return_x = 10, return_y = 15" [/candidate_action]
Available Candidate Actions
- cpp engine: The available CAs of the default AI are given here.
- lua engine: The Micro AIs are written using Lua CAs, which can be used either as they are or as templates for creating your own CAs. They can be found here.
- fai engine: As we said above, we do not recommend using the fai engine any more for creating custom CAs. However, if you want to do so, a few simple Formula AI CA examples can be found in the Formula AI legacy documentation.
The [ai] Tag — Aspects
The values of aspects are used by the candidate actions of the default AI as parameters influencing the AI behavior. Thus, for the most part, they only apply to the CAs of the default AI. It is, however, possible to write Lua CAs which query certain aspects and take them into account. For example, some of the Micro AIs respect some of the default aspects.
A huge number of aspects are available for the default AI. They are therefore given their own page. Only the syntax and internal structure of the aspect configuration is shown here.
There are two types of syntax for defining aspects. In the following, we slowly walk you through these using increasingly complex examples, with the full definition of all the possible key and tags listed at the end of this section.
The vast majority of aspects can be defined using a straight-forward syntax of simply defining their values using keys or tags. These are referred to as standard aspects. Examples are
[ai] time_of_day=dawn aggression=0.765 [/ai]
or
[ai] [avoid] terrain=W* [/avoid] [ai]
For standard aspect syntax, several additional keys are available in addition to the aspect names themselves, time_of_day (as shown in the example above), turns and ai_algorithm. These are also explained at AiWML.
By contrast, a few aspects cannot be defined in such a way and need a more complex structure. These are called composite aspects. AiWML points out when an aspect requires composite aspect syntax and how exactly to set it up. Unless specifically stated otherwise, all other aspects listed there can be defined using standard aspect syntax.
This distinction between standard and composite aspects is only on the WML side though. Internally, the engine treats all aspects as composite and translates the standard aspect definition into a composite configuration snippet. As a result, any standard aspect can also be defined using composite syntax. That is useful, for example, if you want to remove a certain aspect at a later time. You can do that by assigning the aspect an id, which can then be used to be used to remove the aspect.
Let's try to explain this using the aggression example from above. If we wanted to define that using the composite aspect syntax, it would look like this:
[ai] [aspect] id=aggression [facet] id=custom_aggression # Can be used to remove this facet later time_of_day=dawn value=0.765 [/facet] [/aspect] [/ai]
The most important part is that the composite aspect structure uses so-called facets, which contain the value of the aspect. If the aspect is a single scalar value, such as in this example, that value is assigned using the value key. If it is a tag, such as [avoid], the content of the tag is added inside a [value] tag. For example
[facet] engine=cpp id=custom_avoid name=standard_aspect [value] terrain=W* [/value] [/facet]
That’s it for the most practical purposes, although there a few more parameters in the full aspect configuration:
[aspect] engine=cpp id=aggression invalidate_on_gamestate_change=no invalidate_on_minor_gamestate_change=no invalidate_on_tod_change_change=yes invalidate_on_turn_start=yes name=composite_aspect [facet] engine=cpp id= invalidate_on_gamestate_change=no invalidate_on_minor_gamestate_change=no invalidate_on_tod_change_change=yes invalidate_on_turn_start=yes name=standard_aspect time_of_day=dawn turns= value=0.765 [/facet] [default] engine=cpp id=custom_aggression invalidate_on_gamestate_change=no invalidate_on_minor_gamestate_change=no invalidate_on_tod_change_change=yes invalidate_on_turn_start=yes name=standard_aspect time_of_day=dawn turns= value=0.4 [/default] [/aspect]
Thus, the full list of keys and tags inside the [aspect] tag is as follows:
- engine=cpp: The engine processing the aspect. Most often this is 'cpp', which is also the default and can therefore be omitted, but it is also possible to define dynamic Lua aspects. These have the syntax shown below.
- id: This must be set to the aspect name (yes, name) shown at AiWML.
- name=composite_aspect: Must be set to 'composite_aspect', which is also the default. This can therefore be omitted.
- invalidate_on_turn_start="yes": (boolean) If "yes", the value of this aspect is invalidated at the start of each AI turn.
- invalidate_on_tod_change="yes": (boolean) If "yes", the value of this aspect is invalidated when the time of day changes (not working at the moment). This implies invalidate_on_turn_start.
- invalidate_on_gamestate_change="no": (boolean) If "yes", the value of this aspect is invalidated on each game state change (on turn start, move, attack, recruit, etc.). This implies invalidate_on_turn_start.
- invalidate_on_minor_gamestate_change="no": (boolean) If "yes", the value of this aspect is invalidated on each minor game state change (on set unit variable, etc). This implies invalidate_on_gamestate_change.
- code: (Lua engine only) If the aspect is defined dynamically in Lua, this key contains the Lua code to be executed. See below.
- [facet] or [default]: These tags define the aspect values. [default] contains the default value assigned to the aspect at the start of the game. It cannot be modified or deleted. [facet] tags contain the custom values for the aspect and override the value from [default]. If there are several [facet] tags, the most recent one active at the current time of day and turn number is applied. The tags contain the following keys:
- engine: Same as above, overrides the key with same name in the [aspect] tag
- id: This has a different meaning from the [aspect] tag id key. It can be freely chosen and is only needed if you plan to modify or remove the aspect at a later time. If not, the id can be omitted.
- The different invalidation keys: They have the same meaning as those under the [aspect] tag, but apply only to this facet.
- name: Possible values are 'standard_aspect' or 'composite_aspect'. The engine chooses the correct value automatically, so you never have to worry about this.
- time_of_day and turns: Define when the aspect is active. See here for details.
- value or [value]: This, finally, is the actual value the facet returns. See the examples above for syntax.
All of this is admittedly a bit complex. This is done because there are a couple aspects which require all these keys and customization functionality. In the vast majority of cases, you do not have to worry about this and can simply use the standard aspect syntax of the first two examples.
Note: If unsure, the easiest way to see the full configuration for any given aspect (or any other AI component, in fact) is either in savefiles, or in-game by typing :inspect
in debug mode and choosing 'ai config full' for a team.
Some more on the invalidation keys
A few of the aspects return a significant amount of information, as opposed to only a single value (such as aggression) or an array of locations (such as avoid). For example, the attacks aspect returns a list of possible attacks of the filtered attacker/target unit pairs, together with ratings for many different aspects of these attacks. This list, originally established at the beginning of the AI turn, is not valid any more after the first attack has been performed and needs to be reset. The AI does this by "invalidating" the aspect, after which the aspect information is calculated anew the next time the aspect is queried by a candidate action.
If you check out the default values of the invalidation keys, you see that, in the default configuration, information stored about aspects is reevaluated only at the beginning of each AI turn and when the time of day changes. It is not reevaluated by default after the game state changes (such as after an attack) during the AI turn. This works for most aspects, but not for the attacks aspect (and it might not be sufficient for all custom aspects either). This is the reason why invalidate_on_gamestate_change=yes
needs to be set explicitly for the attacks aspect as shown here.
As a final comment, you can see at that link that the attacks aspect is also an exception in that its configuration and definition do not contain a value key or [value] tag, but [filter_own] and [filter_enemy] tags.
Dynamic Lua Aspects
It is also possible to define aspects dynamically in Lua. In that case, no [default] and [facet] tags are added to the configuration. They are replaced by the code key used to define the aspect. For the second example given at the link above, the configuration looks like this:
[aspect] code="local value = wesnoth.current.turn / 10. return value " engine=lua id=aggression invalidate_on_gamestate_change=no invalidate_on_minor_gamestate_change=no invalidate_on_tod_change_change=yes invalidate_on_turn_start=yes name=composite_aspect [/aspect]
The [ai] Tag — Goals
Goals are essentially a special type of aspect, in that they are used to customize the behavior of the default (RCA) AI. However as described here, the default goals are not stored in configuration files. Similarly, custom goals are stored in a different format in the AI configuration than the aspects. Fortunately, this format is the same as that used for defining custom goals. See the examples given there for syntax.
The [ai] Tag — Engines
As stated above, it is not necessary to define engines any more these days. Just for reference, we list the possible attributes of the [engine] tag here nevertheless.
- id: (string) Optional id of the engine.
- engine=cpp: (string) The type of engine used to parse this [engine] config snippet. This will always be cpp, since the lua and fai engines are not equipped for parsing [engine] tags.
- name=cpp: (string) Name of the [engine]. Must be a valid engine name (cpp, lua, or fai).
- code: If
name=lua
only. The Lua code to add to the engine. See the old method for setting up custom Lua CAs for details. It should generallyreturn ...
or similar - if it doesn't, other components using the Lua engine may not be able to function correctly.
See also: Wesnoth AI