RCA AI

From The Battle for Wesnoth Wiki
Revision as of 14:28, 17 February 2016 by Mattsc (talk | contribs) (Undo revision 57125 by Mattsc (talk))
This page is currently under construction (Feb 2016)

RCA AI Summary

The RCA (Register Candidate Action) AI is the default AI since Wesnoth 1.9. Its name stems from the fact that it has a list of potential actions, called candidate actions (CAs), that are evaluated for each move and are executed in order of evaluation score. The RCA AI is highly configurable in WML. It can also be modified by removing default CAs or adding custom CAs.

This page describes how the RCA AI is assembled out of the different components of Wesnoth's composite AI framework. For related information also see:

RCA AI components

The RCA AI utilizes the following components:

  • The main_loop stage with its candidate action evaluation loop.
  • Candidate actions are the potential actions the AI might take during an AI turn, such as attacking, village grabbing, recruiting or retreating injured units.
  • Aspects are the parameters which can be used to configure the AI behavior.
  • Goals are a specific type of aspect which define targets toward which the AI moves. It is very important to understand that these are only move-to targets, not attack targets.

The use of each of these components in the RCA AI is described in the following, with descriptions of the methodology used in the subsections of this section, and the configuration setup farther below. For general descriptions of what these components are in the Wesnoth AI framework, see here.

The Candidate Actions (CAs) of the RCA AI

For each move of its turn, the AI evaluates a number of potential actions, called candidate actions (CAs). The CA with the highest evaluation score is executed and the process is repeated, until no more valid moves are found. This ends the AI turn.

The scores of the RCA AI have fixed values. These values can be modified, but only to another fixed value. For example, the recruitment CA by default has a score of 180,000. The default score for attacks is 100,000. Thus, if recruiting is possible, it will always be done before attacks. However, that does not mean that all recruiting will always happen before all attacks.

Consider the case that all castle hexes are occupied by AI units which were recruited on a previous turn and have not been used yet. No recruiting is possible, so the AI moves on to other CAs. Once it gets to attacks, one of the units (or a group of units) might be selected to move off the castle and attack a close-by enemy. Now a castle hex is available for recruiting, which has then the highest evaluation score. This cycle might be repeated several times.

In its default configuration, the RCA AI evaluates the following CAs, sorted in order of their evaluation score:

  • Goto CA (score 200,000): Move units toward the coordinates set by goto_x, goto_y in their unit WML.
  • Recruitment CA (score 180,000): Recruit or recall units.
  • Move leader to goals CA (score 140,000): Move the leader toward goals set by [leader_goal].
  • Move leader to keep CA (score 120,000): Move the leader toward the closest available keep.
  • Combat CA (score 100,000): Attack enemy units that are in range. Note that this CA includes the move to a hex adjacent to the enemy unit as well as the attack itself.
  • Healing CA (score 80,000): Move units onto healing locations (generally villages).
  • Villages CA (score 60,000): Move units onto villages that are unowned or owned by an enemy.
  • Retreat CA (score 40,000): Evaluate if any units are in grave danger or hopelessly outnumbered, and if so, retreat them.
  • Move to targets CA (score 20,000): Evaluate potential targets and move units toward them. The evaluation takes into account how valuable the targets are (this is configurable by setting goals), how easily the AI's units can get there, how exposed to attacks they will be, etc. Targets are enemy leaders, enemy units that pose a threat to the AI leader and villages, as well as anything defined by the [goal] tag. It is important to understand that targets only apply to the move-to-targets CA. If a target is within range of the AI's unit(s) on the current turn, other CAs, such as combat, healing and villages have a higher evaluation score and are (usually) executed first. Thus, a target is specifically not an attack target, but a move target.
  • Leader shares keep CA (score 10,000): Move the leader off the keep to let an allied leader recruit, if an allied leader can get to the keep in one move. Note: This CA used to be called 'passive_leader_shares_keep'. That name still works for the time being but will be deprecated at some point.

As already mentioned, the CAs are always evaluated (and executed, if a possible move is found) in the order given above. For example, the Goto CA is always the first move that is done (if goto_x and goto_y are defined and a move toward them is possible). Equivalently, if a valid attack is found in the combat CA, it is always executed before any healing, village grabbing or move-to-target moves. One of the effects this has is that attacks always have priority over any goals/targets of the move-to-target phase. It is very important to understand this, as it is a frequent source of confusion for scenario creators.

Note that nothing in Wesnoth's general AI mechanism limits CAs to have fixed evaluations scores. For examples, some of the Micro_AIs as well as some custom AI CAs return variable scores.

Use of Aspects in the RCA AI

An aspect is simply a parameters that the AI provides to scenario creators for configuring the AI behavior. For example, one can change how aggressive/careful the AI acts, how recruiting is prioritized or whether the leader participates in attacks.

The RCA AI has a large number of aspects. Their description is therefore given its own page.

Use of Goals in the RCA AI

Goals, which define AI targets, are a special type of aspects that influence the behavior of the AI in the move-to-targets candidate action.

AI targets are used in the move-to-targets candidate action to move the AI's units toward selected units or locations. The AI engine automatically selects all enemy leaders, enemy units that pose a threat to the AI leader and unowned or enemy-owned villages as targets and assigns them certain base values. A score is then assigned to each target based on this base value, as well as on the movement cost required to get to the target, whether moving there would put the units involved in danger, etc.

It is possible to define additional targets or influence the relative ratings of the default targets. This is done with the [goal] tag, in which we can set criteria selecting targets and define their base values (which are then evaluated in the same way as the values of the default targets). Values set with the [goal] tag should always be relative to each other. The AI is willing to dedicate twice as many resources and travel twice as far to get to a target worth '2.0' as for a target worth '1.0'.

We stress again that these targets apply to the move-to-targets CA only and have no influence on other CAs. This is significant since CAs that deal with, for example, combat or village-grabbing have a higher score than the move-to-targets CA and are therefore always executed first. In practice that means that targets set with the [goal] tag only affect the AI behavior for targets that it cannot reach during the current turn, and only after combat, village-grabbing etc. are finished.

Five types of targets can be set using the [goal] tag: target (the default), target_location, protect_unit, protect_my_unit, and protect_location. As there a several different goals available, they are given their own subsection on the AI aspects page.

Customizing the Default AI

Modifications of the AI behavior can be done in a variety of different ways:

  • Changing the existing candidate actions:
    • Modifying the evaluation scores of the CAs
    • Deleting candidate actions
    • See below for more details on this
  • Adding new candidate actions:
    • Writing new candidate actions using LuaAI
    • Writing new candidate actions using FormulaAI
    • It is, in principle, also possible to change existing or add new CAs by modifying the C++ source code. This is, of course, not useful for dynamically altering AI behavior in existing Wesnoth versions, but it can be done for committing bug fixes or new features to future versions of Wesnoth.

Before explaining how to modify the candidate actions, we first need to understand how the main_loop stage and its candidate actions are set up in Wesnoth.

Default AI Configuration

Stages

The RCA AI is defined in file ai/ais/ai_default_rca.cfg in the Wesnoth data directory and only consists of the main_loop stage:

[ai]
    id=ai_default_rca
    description=_"Multiplayer_AI^Default AI (RCA)" # wmllint: no spellcheck
    version=10710
    [stage]
        id=main_loop
        name=testing_ai_default::candidate_action_evaluation_loop
        {AI_CA_GOTO}                         # id="goto"
        {AI_CA_RECRUITMENT}                  # id="recruitment"
        {AI_CA_MOVE_LEADER_TO_GOALS}         # id="move_leader_to_goals"
        {AI_CA_MOVE_LEADER_TO_KEEP}          # id="move_leader_to_keep"
        {AI_CA_COMBAT}                       # id="combat"
        {AI_CA_HEALING}                      # id="healing"
        {AI_CA_VILLAGES}                     # id="villages"
        {AI_CA_RETREAT}                      # id="retreat"
        {AI_CA_MOVE_TO_TARGETS}              # id="move_to_targets"
        {AI_CA_PASSIVE_LEADER_SHARES_KEEP}   # id="passive_leader_shares_keep"
     [/stage]
[/ai]

Note: Before the RCA AI became the default it was necessary to include the file (or one of its aliases) in the side definition. That is not necessary any more, it is automatically included for all computer controlled sides.

Candidate Actions

Each candidate action is included as a macro and has the following syntax (combat CA example):

[candidate_action]
    id=combat
    engine=cpp
    name=ai_default_rca::combat_phase
    max_score=100000
    score=100000
[/candidate_action]

See above for descriptions of the individual CAs and their scores. See file core/macros/ai_candidate_actions.cfg for the definitions of all the macros and the ids and names of the CAs.

Aspects

As far as defining aspect with WML is concerned, aspects can be either simple or composite. Simple aspects are those that take on a single (scalar) value. They can be defined in WML with a single line of code inside an [ai] tag. For example

[ai]
    aggression=0.765
[/ai]

By contrast, composite aspects are those that do not simply consist of a single key=value pair, either because their structure is more complex (such as a tag) or because additional information is included (such as an aspect id or information as to under what conditions the aspect is applied.

This distinction is only on the scenario creator side though. Internally, the engine treats all aspects as composite and translates the simple aspect definition into a configuration snippet for the aspect component. For the example above, the relevant parts of the configuration look like this:

[aspect]
    engine=cpp
    id=aggression
    [facet]
        engine=cpp
        name=standard_aspect
        value=0.765
    [/facet]
    [default]
        engine=cpp
        name=standard_aspect
        value=0.4
    [/default]
[/aspect]

This shows the [aspect] tag containing information on which engine should be used to interpret it and its id. The follow the so-called ‘facet’ tags, which define the values the engine returns for the aspect. There is always a [default] facet, which returns the default value of the aspect (0.4 in the case of aggression). In addition, one or several custom facets can be included. In our example, we see how the new value of 0.765 defined above is included in the config.

That’s it for the most part, but even that it is actually not the full aspect config yet. That looks like this:

[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=
        turns=
        value=0.765
    [/facet]
    [default]
        ...  (omitted for brevity, same as above)
        value=0.4
    [/default]
[/aspect]

Thus, we see, that a facet may also contain an id (important if one wants to delete it selectively), the time of day and turns for which it is valid, and several invalidate_on_... keys. The AI code stores information derived from the aspects in C++ objects. This information can simply be the aspect value for some aspects (such as aggression) or it can contain a significant amount of additional information. For example, for the attacks aspect, it contains a list of all possible attacks of the filtered attacker/target unit pairs. 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 read anew from the [ai] tags and the stored information (the list of attacks, in this case) is reevaluated.

There are 4 levels of invalidation keys:

  • 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.

Thus, information stored about aspects is reevaluated by default at each AI turn and when the time of day changes. It is not reevaluated after the game state changes (such as after an attack), which explains why 'invalidate_on_gamestate_change=yes' needs to be set explicitly for the attacks aspect.

Hint: If unsure, the easiest way to see the full config 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.

Goals

Goals are simply a special type of aspect, so they are stored in configs in exactly the same way as shown above. The only difference from many of the other aspects is that they can never be defined by a single key=value pair. Thus, they are always composite aspects.

Modifying the RCA AI Candidate Actions