Difference between revisions of "LuaAI"

From The Battle for Wesnoth Wiki
(Preload event)
m (fix errors (title levels))
(46 intermediate revisions by 3 users not shown)
Line 1: Line 1:
This is a page containing information on configuring the AI using Lua.  In addition to the technical information on this page, a tutorial-style guide is also available at [[Lua AI Howto]].  
+
Wesnoth provides the functionality to both access and define all types of [[Wesnoth_AI_Framework#Types_of_AI_Components|AI components]] using [[LuaWML|Lua]]Instructions for using these components to customize the default AI and to create custom AIs are explained on other pages.  See [[Wesnoth AI]] for a summary of the available documentation.  This page is a reference guide to the Lua AI functions, variables and component syntax.
  
NB: previous contents of the page moved to http://wiki.wesnoth.org/LuaAI(old)
+
== Lua AI Functions ==
  
== Aspects ==
+
The code of [[Creating_Custom_AIs#Creating_Custom_Candidate_Actions|custom Lua candidate actions]] (CAs) has access to the ''ai'' table. This is a Lua table containing functions for getting information about the game state and for executing AI moves. Together with the other [[LuaWML|Wesnoth Lua functions]] they can be used in the CA [[Creating_Custom_AIs#Evaluation_Function|evaluation]] and [[Creating_Custom_AIs#Execution_Function|execution]] functions to code the desired AI behavior.
Patch r49721 enabled users to write aspects using Lua. This simplifies the definition of aspects that are meant to change depending on the game state. A good example could be aggression, which changes depending on the time of the day(since ToD affects the actual battle performance of your forces).<br />
 
Static aggression:
 
[aspect]
 
id="aggression"
 
engine="lua"
 
value="0.3"
 
[/aspect]
 
Dynamic aggression:
 
[aspect]
 
id=aggression
 
engine=lua
 
   
 
code=<<
 
wesnoth.fire("store_time_of_day")
 
local value = 0
 
tod = tostring(wesnoth.get_variable('time_of_day').name)
 
if (tod == 'Morning') then
 
value = 0.2
 
else
 
value = 1
 
end
 
 
wesnoth.fire("clear_variable", {name = "time_of_day"})
 
return value
 
>>     
 
[/aspect]
 
  
Note: the way of getting the ToD is hacky here, but I will create a more elegant method soon(Nephro).<br />
+
=== ''ai'' Table Functions Returning Game State Information ===
Also note the difference between 'code' and 'value' attributes, this is important.<br /><br />
 
At the moment, it is possible to create the following aspects using Lua(the list will be constantly updated):
 
advancements                                // string[] or function (see below)
 
aggression                                  // double
 
attack_depth                                // int
 
avoid                                        // exposed as map_location[], where map_location is a table of form {x, y}
 
caution                                      // double
 
grouping                                    // string
 
leader_aggression                            // double
 
leader_goal                                  // exposed as a config
 
leader_value                                // double
 
number_of_possible_recruits_to_force_recruit // double
 
passive_leader                              // bool
 
passive_leader_shares_keep                  // bool
 
recruitment_ignore_bad_combat                // bool
 
recruitment_ignore_bad_movement              // bool
 
recruitment_pattern                          // string[]
 
scout_village_targeting                      // double
 
simple_targeting                            // bool
 
support_villages                            // bool
 
village_value                                // double
 
villages_per_scout                          // int
 
  
 +
The ''ai'' table contains a large number of functions returning game state information.  Some of those are simple scalar values which are self-explanatory, while others can be tables with many elements and complex structure.  We will not show the structure of all these tables here.  If you need to know the exact structure of such a table, use the method described below for [[#Debug_access_to_the_AI_tree|accessing the AI tree]].
  
 +
==== Retrieving Aspect Values ====
  
 +
The contents of many of the [[AiWML#List_of_AI_Aspects|AI aspects]] can be read using ''ai'' table commands of the form
 +
* '''ai.get_<aspect_name>()''', for example
 +
** '''ai.get_aggression()'''
 +
** '''ai.get_caution()'''
 +
Note that this returns the result of the aspect evaluation given the game state at the time of the last [[Wesnoth_AI_Framework#Some_more_on_the_invalidation_keys|invalidation of the aspect]], not its definition.  For example, '''ai.get_avoid()''' returns the list of avoided hexes, rather than the [[StandardLocationFilter|Standard Location Filter]] used to find them, and [[#Dynamic_Lua_Aspects|dynamic Lua aspects]] return the function evaluation result, not the function itself.
  
 +
The full list of functions returning aspect values is
 +
Functions returning scalar values (numbers, strings or booleans):
 +
ai.get_aggression()
 +
ai.get_attack_depth()
 +
ai.get_caution()
 +
ai.get_grouping()
 +
ai.get_leader_aggression()
 +
ai.get_leader_ignores_keep()
 +
ai.get_leader_value()
 +
ai.get_number_of_possible_recruits_to_force_recruit()
 +
ai.get_passive_leader()
 +
ai.get_passive_leader_shares_keep()
 +
ai.get_recruitment_ignore_bad_combat()
 +
ai.get_recruitment_ignore_bad_movement()
 +
ai.get_scout_village_targeting()
 +
ai.get_simple_targeting()
 +
ai.get_support_villages()
 +
ai.get_village_value()
 +
ai.get_villages_per_scout()
 +
 +
Functions returning tables:
 +
ai.get_attacks()
 +
ai.get_avoid()
 +
ai.get_leader_goal()
 +
ai.get_recruitment_pattern()
 +
 +
{{DevFeature1.13|5}}
 +
 +
These functions are now deprecated. The new standard way of accessing aspect values is through the <code>ai.aspects</code> table, for example <code>ai.aspects.aggression</code>. All aspects can be accessed this way, including some that don't have function forms, such as the advancements aspect. {{DevFeature1.13|6}} The corresponding get_aspect_name functions are now deprecated, with the exception of <code>ai.get_attacks()</code> which returns an analysis of all possible attacks; this differs from <code>ai.aspects.attacks</code> which only returns the allowed units for use in attacks (in "own" and "enemy" subtables).
 +
 +
==== Functions Returning Other Game State Information ====
 +
 +
* '''ai.get_targets()''': Get the AI targets.  See [[#Lua_Goals_and_Targets|below]] for details.
 +
* '''ai.side''': side number of the AI
 +
* '''ai.suitable_keep(''unit'')''': This returns the location of the closest keep to the unit passed as argument.  Note that this returns the ''x'' and ''y'' coordinates of the keep as two separate arguments, so it needs to be called as <syntaxhighlight lang='lua' inline>local x, y = ai.suitable_keep(unit)</syntaxhighlight>.
 +
 +
Finally, the following functions related to move maps (tables that relate the current positions of units and the hexes they can move to) also exist:
 +
* '''ai.get_new_dst_src()'''
 +
* '''ai.get_new_enemy_dst_src()'''
 +
* '''ai.get_new_enemy_src_dst()'''
 +
* '''ai.get_new_src_dst()'''
 +
* '''ai.is_dst_src_valid()'''
 +
* '''ai.is_enemy_dst_src_valid()'''
 +
* '''ai.is_enemy_src_dst_valid()'''
 +
* '''ai.is_src_dst_valid()'''
 +
* '''ai.recalculate_enemy_move_maps()'''
 +
* '''ai.recalculate_move_maps()'''
 +
If you need to know what they do and the exact structure of their return values, use the method described below for [[#Debug_access_to_the_AI_tree|accessing the AI tree]].
 +
 +
The file ''ai/lua/stdlib.lua'' replaces the move-map functions with the following simplified set:
 +
* '''ai.get_dst_src()'''
 +
* '''ai.get_src_dst()'''
 +
* '''ai.get_enemy_dst_src()'''
 +
* '''ai.get_enemy_src_dst()'''
 +
 +
'''Note:''' The file stdlib.lua is loaded by default, but if a custom Lua [engine] tag is defined, it must load the file manually with the following code:
 +
<syntaxhighlight lang='lua'>
 +
local ai_stdlib = wesnoth.require('ai/lua/stdlib.lua')
 +
ai_stdlib.init(ai)
 +
</syntaxhighlight>
 +
 +
=== ''ai'' Table Functions for Executing AI Moves ===
 +
 +
For all functions below, units (''unit'', ''attacker'', ''defender'') can be passed either as a unit object in a single variable, or as two parameters specifying the unit's ''x'' and ''y'' coordinates.  For functions taking more than one unit as arguments, it can also be a mix of the two.
  
Note: simple numeric, bool and string aspect creation can be enabled just by adding a specific factory to registry.cpp, this will also soon be done.
+
* '''ai.attack(''attacker, defender, weapon, aggression'')''': Execute attack by ''attacker'' against ''defender''. If ''weapon'' is provided, it is the number of the weapon to be used (count starts at 1, not 0, as always in Lua), otherwise choice of the weapon is left to the AI engine.  If ''aggression'' is provided, it is used to influence the choice of the best weapon.  Obviously, this only makes sense if this choice is left to the engine, that is, if ''weapon'' is set to either 'nil' or '-1'.
 +
* '''ai.move(''unit, to_x, to_y'')''': Execute partial move of ''unit'' to hex ''(to_x'', ''to_y)''.  ''Partial'' move means that the unit keeps whatever movement points it has left after the move.
 +
* '''ai.move_full(''unit, to_x, to_y'')''': Execute full move of ''unit'' to hex ''(to_x'', ''to_y)''.  ''Full'' move means that the unit's movement points are set to zero at the end of the move.
 +
* '''ai.recall(''unit_id, x, y'')''': Recall the unit with id ''unit_id''.  An optional recruit location can be given in ''x'' and ''y''.  If the location is omitted, a suitable hex is automatically chosen by the AI engine.
 +
* '''ai.recruit(''unit_type, x, y'')''': Recruit a unit of type ''unit_type''. An optional recruit location can be given in ''x'' and ''y''.  If the location is omitted, a suitable hex is automatically chosen by the AI engine.
 +
* '''ai.stopunit_attacks(''unit'')''': Remove remaining attacks from ''unit''.  This is equivalent to setting <code>attacks_left=0</code>.
 +
* '''ai.stopunit_moves(''unit'')''': Remove remaining movement points from ''unit''.  This is equivalent to setting <code>moves=0</code>.
 +
* '''ai.stopunit_all(''unit'')''': Remove both remaining attacks and remaining movement points from ''unit''.
 +
* '''ai.synced_command(''command, x1, y1'')''': Execute the specified Lua command in a replay and MP safe way. ''command'' is a string containing the Lua code and ''(x1,y1)'' is an optional map location that is the only allowed external variable used in the code string (specified as Lua variables ''x1'' and ''y1'').
  
To get the value of a specific aspect in Lua we have to use the
+
{{DevFeature1.13|5}} The above functions change the gamestate and thus are not always available. To be more specific, they are only available if '''ai.read_only''' is false. If it is true, the functions do not exist. This is the case everywhere except stage and candidate evaluation functions.
ai.get_<aspect_name>()
+
The following functions are available for checking whether a planned AI move is possible. This is useful in order to prevent an aborted execution of a move, which might result in some or all of the remaining moves to be aborted by the AI due to a [[Creating_Custom_AIs#Execution_Function|blacklisted candidate action]].
function. E.g.
 
  local aggression = ai.get_aggression()
 
  
===Aspect: advancements===
+
* '''ai.check_attack()
This aspect can be used to define to what unit a specific unit on the map should advance.
+
* '''ai.check_move()
It can be used in multiple ways:
+
* '''ai.check_recall()
<ol>
+
* '''ai.check_recruit()
<li> Lua returns a String of the Form "Javelineer, Footpad". <br />When the AI advances a Spearman he will become a Javelineer (because Javelineer is a member of the list).
+
* '''ai.check_stopunit()
</li>
+
* '''ai.check_synced_command()
<li>Lua returns a function of the Form <code>advance(x, y)</code> which itself returns a string.</li>
 
  [ai]
 
[aspect]
 
id=advancements
 
engine=lua
 
 
code=<<
 
function advance(x, y)
 
local unit = wesnoth.get_unit(x, y)
 
if(unit.id == 'bob') then
 
return 'Swordsman'
 
else
 
return 'Javelineer'
 
end
 
end
 
return advance
 
>>
 
[/aspect]
 
[/ai]
 
When a unit advances <code>advance(x, y)</code> is called with the current location of the unit as parameter. You may use <code>wesnoth.get_unit(x, y)</code> to retrieve the unit object itself.
 
Note that the return value of <code>advance(x, y)</code> could also be a list (like in 1.).
 
<li>
 
Just for completeness: The aspect will work without Lua and can then look like this:
 
</li>
 
[ai]
 
advancements = "Javelineer, Footpad"
 
[/ai]
 
  
</ol>
+
These check functions take the same arguments as the respective AI action functions.  They return a Lua table which looks something like the following for a successful check
Note that this aspect only affects attacking units. When a defending unit advances it is still decided randomly. That is because Wesnoths design don't allow the AI to make out of turn decisions.
+
<syntaxhighlight lang='lua'>{
 +
    gamestate_changed = false,
 +
    ok = true,
 +
    status = 0,
 +
    result = 'action_result::AI_ACTION_SUCCESS'
 +
}</syntaxhighlight>
 +
and like this for an unsuccessful check
 +
<syntaxhighlight lang='lua'>{
 +
    gamestate_changed = false,
 +
    ok = false,
 +
    status = 2008,
 +
    result = 'move_result::E_NO_ROUTE'
 +
}</syntaxhighlight>
  
== Preload event ==
+
'''Notes:'''
 +
* ''status'' is the error code returned by the AI engine in case of an unsuccessful code.  The error string for a given error code can be found in file [https://github.com/wesnoth/wesnoth/blob/master/src/ai/actions.hpp src/ai/actions.hpp]. {{DevFeature1.13|5}} It is now also available as the ''result'' element of the table.
 +
* The move execution functions return the same table, with the only difference being that ''gamestate_changed'' is 'true' in case the execution resulted in a change of the game state (even if the move was only partially successful).
  
It is a good idea to have such a preload event in your scenario if you are intending to use Lua for AI programming or customizing. The code of this event will most probably be moved out to a macro(except for the line that requires patrol.lua, since it is not necessary).
+
== Lua AI Variables — Persistence ==
  
[event]
+
When [[Creating Custom AIs]] in Lua, it is sometimes necessary to transfer information between the evaluation and execution functions of the same candidate action (CA) or between CAs. It might also be necessary to have this information persist from turn to turn and across save/load cyclesThis could, in principle, be done using WML variables, but depending on the type and amount of information, that could be awkward and/or inconvenient to do in practice.
    name=preload
 
    first_time_only=no
 
    [lua]
 
        code = <<
 
            H = wesnoth.require "lua/helper.lua"
 
            W = H.set_wml_action_metatable {}
 
            _ = wesnoth.textdomain "my-campaign"
 
    ai = {}
 
    ca_counter = 0
 
   
 
    wesnoth.require("ai/lua/patrol.lua")
 
 
    >>
 
[/lua]
 
[/event]
 
  
The code attribute can be further expanded by any Lua code needed. For example, we can set up some global variables there or include additional code libraries("wesnoth.require("ai/lua/patrol.lua")" - includes code that handles patrolling of units).
+
The Wesnoth AI therefore includes a field called ''data'' which is automatically added to the internal AI context.  The evaluation and execution functions of [[Creating_Custom_AIs#Setting_Up_a_Custom_AI|Lua candidate actions]] have access to this field if the third (optional) variable is passed to them. Assuming this variable is called ''self'', the field is accessed as ''self.data''.
  
== Engine code ==
+
When saving a game, the save routine finds this field, converts it to a config and stores its content in readable format in the save file.  When the scenario gets loaded back, the engine code injects the data back into the AI context.  Note that, in order for this to work, all content of ''data'' needs to be in [[LuaWML#Encoding_WML_objects_into_Lua_tables|WML table format]].  Other format content can also be used during an AI turn, for example to pass it between the evaluation and execution functions of the same CA, but it is lost when saving the game.
  
In the engine tag you should define functions that will handle evaluation and execution of your custom candidate actions and stages. You can also run some code that is intended to be run once in the beginning.<br />
+
== Dynamic Lua Aspects ==
The definition of the [engine] tag should be place inside the [ai] tag.
 
  
  [engine]
+
It is possible to define aspects using Lua. These can be either static or dynamic, that is, their value can change depending on the game state. We demonstrate this here with a few examples.
    name="lua"
 
    code= <<
 
        -- your engine code here
 
    >>
 
[/engine]
 
  
== Stages ==
+
First, as simple example of defining the ''aggression'' aspect statically in Lua:
Once the engine has been set up, we can start adding stages to the configuration of our AI.<br />
+
<syntaxhighlight lang='wml'>
To add a stage we just use the [stage] tag.
+
[aspect]
[stage]
+
    id="aggression"
    engine=lua
+
    engine="lua"
    code=<<
+
    value="0.765"
        -- Code for stage execution here
+
[/aspect]
        -- It is better to call predefined functions for the [engine] code,
+
</syntaxhighlight>
        -- than to define the functions here, to keep the structure of the stages clear
+
There is, of course, no advantage of this over simply [[AiWML|defining the aspect in WML]], we simply show it for completeness and for the syntax.  An example of defining aggression dynamically so that it increases slowly from turn to turn is
    >>
+
<syntaxhighlight lang='wml'>
[/stage]
+
[aspect]
 +
    id=aggression
 +
    engine=lua
 +
   
 +
    code=<<
 +
        local value = wesnoth.current.turn / 10.
 +
        return value
 +
    >>    
 +
[/aspect]
 +
</syntaxhighlight>
 +
Note the difference between the ''value'' and ''code'' attributes in the two examples.
  
== Candidate actions ==
+
{{DevFeature1.13|5}} A Lua aspect may also contain an [args] subtag, which is passed to the aspect evaluation function in the same way as for candidate actions.
  
{{devfeature1.11}}  In addition to the method described below, it is now also possible to use [[Lua_AI:_External_Candidate_Actions|external candidate actions]].
+
==== The ''advancements'' Aspect ====
  
This is an example from the current version of the lua_ai arena. The stage with an id "ca_loop" is the RCA AI loop stage, as we can see from its name. Currently it has two candidate actions, both Lua backed.  
+
A slightly more elaborate example, using the ''advancements'' aspect, is given below.  This aspect is different from others in that it can be used with a function ''advance(x, y)'', where ''(x, y)'' is the current location of the unit about to advance. You can then use ''wesnoth.get_unit(x, y)'' to retrieve the unit object itself. For example, the following code advances the unit with id 'Bob' to a swordsman (assuming here that this unit is a spearman), while all other spearmen are advanced to javelineers.
 +
<syntaxhighlight lang='wml'>
 +
[ai]
 +
    [aspect]
 +
        id=advancements
 +
        engine=lua
  
            [stage]
+
        code=<<
                name=testing_ai_default::candidate_action_evaluation_loop
+
            function advance(x, y)
id=ca_loop
+
                local unit = wesnoth.get_unit(x, y)
                [candidate_action]
+
                if (unit.id == 'Bob') then
                    engine=lua
+
                    return 'Swordsman'
                    name=first
+
                else
    id=firstca
+
                    return 'Javelineer'
                    evaluation="return (...):candidate_action_evaluation_hello()"
+
                end
                    execution="local ai, cfg = ...; ai:candidate_action_execution_hello(cfg)"
+
            end
                [/candidate_action]
+
            return advance
                [candidate_action]
+
        >>
                    engine=lua
+
    [/aspect]
                    name=second
+
[/ai]
                    evaluation="return (...):candidate_action_evaluation_hello2()"
+
</syntaxhighlight>
                    execution="(...):candidate_action_execution_hello2()"
+
Note that this aspect only affects AI units while they are attacking. When a defending unit advances, it is still decided randomly. That is because Wesnoth's design does not allow the AI to make out of turn decisions.
                [/candidate_action]
 
            [/stage]
 
Basically, we need to have an engine attribute, an evaluation and execution functions. The syntax (...):foo() means "call eng.foo(eng) where eng is whatever the engine code returns". We can also add, remove, modify candidate actions on the fly, using wesnoth.wml_actions.modify_ai() functionality. Behavior(sticky) candidate actions use this approach.
 
  
== Behavior(sticky) candidate actions ==
+
==== The ''attacks'' Aspect ====
Sometimes we need a specific unit to do a specific action in our scenario, e.g. we want a scout unit to patrol between 3 places on the map, to provide us with visibility needed. In this case we can create a sticky candidate action that will tie itself to the unit, and remove itself when the unit has died. The syntax for defining a sticky candidate action is the following:
 
[event]
 
name=side 2 turn 1
 
first_time_only=yes
 
        [add_ai_behavior]
 
            side=2
 
            [filter]
 
                name="Rark"
 
            [/filter]
 
  sticky=yes
 
  loop_id=ca_loop
 
            evaluation="return patrol_eval_rark()"
 
            execution="patrol_rark()"
 
        [/add_ai_behavior]
 
[/event]
 
Here the behavior CA is added to the configuration of the AI when the event triggers. Obviously this will happen when side 2 gets its first turn, but the event can be whatever needed. The definition is very similar to a simple candidate action, but here we also filter out a unit and state that we want the behavior to be sticky. If we don't, the action will not try to remove itself, when the unit gets killed, and this could cause errors.<br />
 
Such CAs can also be added from inside other CA or stage code using wesnoth.wml_actions.add_ai_behavior(cfg) function.
 
  
Sticky candidate actions never execute more than once per turn. Therefore, if the action consists of a series of goals, the CA must evaluate and execute each goal as it is achieved within itself and not rely on being called multiple times.
+
{{DevFeature1.13|5}}
  
== Behavior function library ==
+
The attacks aspect may also be defined using the Lua engine; this is a little more complicated than most aspects, though. The aspect must return a table containing two keys, '''own''' and '''enemy'''. The value of each of these keys can be either a table defining a valid WML unit filter or a function which, when called with a unit proxy as its argument, returns true if the unit may be attacked and false otherwise.
Behaviors are defined using generators. A behavior generator receives arguments(unit info, configuration, etc) and returns a pair of functions: evaluator and executor. These functions are to be passed to the add_ai_behavior tag, or directly to the function.
 
=== Patrolling ===
 
The only behavior defined at the moment is the patrol behavior. After calling patrol_gen(name, locations), we receive a pair of functions to use for creation of candidate actions<br />
 
Inclusion of the patrolling code:
 
wesnoth.require("ai/lua/patrol.lua")
 
Usage:
 
local patrol_rark = patrol_gen("Rark", {{x=14, y=7}, {x=15, y=7}, {x=15, y=8}, {x=14, y=8}})
 
-- patrol_rark.exec -- execution function
 
-- patrol_rark.eval -- evaluation function
 
As you can see, the first argument is the name of the unit, the second is a table of coordinates of arbitrary length.
 
  
==Goals and targets==
+
== Lua Goals and Targets ==
  
When deciding what move to do next, the AI uses targets(markers on the map, pointing to villages, units, locations, etc). Targets are produced by goal objects. There are multiple predefined goal objects of different types in the C++ implementation, but now it is possible to define goal objects in Lua.
+
It is also possible to define AI [[RCA_AI#Use_of_Goals_and_Targets_in_the_RCA_AI|goals and targets]] in Lua.  This is, however, somewhat inconvenient and we generally recommend using the [[AiWML#The_.5Bgoal.5D_Tag|[goal] tag]] with WML [[StandardUnitFilter|Standard Unit Filter]]s (SUFs) or [[StandardLocationFilter|Standard Location Filter]]s (SLFs) instead. That is much more versatile and it makes no difference to the AI behavior whether the goals are defined using WML or Lua.
  
[goal]
+
The only time that defining goals in Lua might be advantageous is if one wants to use goals with dynamic values, such as in this example, where one goal has a constant value, while the other's value increases from turn to turn; or if one wants to define other types of targets that the other goals do not support (not demonstrated here).
name=lua_goal
+
<syntaxhighlight lang='wml'>
value=6
+
[goal]
engine=lua
+
    name=lua_goal
code = <<
+
    engine=lua
local t = {
+
    code = <<
    t[1] = {value=2.3, type=3, loc={x=5, y=6} }
+
        local turn = wesnoth.current.turn
    t[2] = {value=2.4, type=4, loc={x=4, y=16} }
+
        local targets = {}
  }
+
        targets[1] = { value = 2, type = 2, loc = { x = 5, y = 6 } }
  return t
+
        targets[2] = { value = turn * 2 , type = 2, loc = { x = 4, y = 9 } }
  >>
+
        return targets
[/goal]
+
    >>
 +
[/goal]
 +
</syntaxhighlight>
 
   
 
   
As you can see, the return value must be a table containing 0 or more targets. Each target must contain the three fields: "loc", "type", "value". This code will then be parsed by the Lua engine and the targets will be available to use to any engine desired. To get the targets inside Lua, a simple
+
As you can see, the return value must be an array containing the targets. Each target must contain three fields:
local tg = ai.get_targets()  
+
* '''value''': The value of the target, equivalent to that defined for WML goals
must be called. tg will contain a table of the same format, as one described in the goal definition code upper.
+
* '''loc''': A table with attributes ''x'' and ''y'' defining the target's location. This is one of the disadvantages with respect to the WML syntax.  Each goal needs to be defined individually by its coordinates.  The use of SUFs and SLFs is not possible.
 +
* '''type''': A number defining the type of the target.  File [https://github.com/wesnoth/wesnoth/blob/master/src/ai/default/contexts.hpp src/ai/default/contexts.hpp] defines the available types as
 +
<syntaxhighlight lang='cpp'>
 +
enum TYPE { VILLAGE, LEADER, EXPLICIT, THREAT, BATTLE_AID, MASS, SUPPORT };
 +
</syntaxhighlight>
 +
User-defined targets should usually be given <code>type = 2</code> for ''target'' and ''target_location'' type targets, and <code>type = 3</code> for ''protect_*'' type targets. This corresponds to ''EXPLICIT'' and ''THREAT'' targets (the third and fourth in the enum list, with counting starting at 0).
 +
{{DevFeature1.13|5}} Target types can now be specified as strings instead of integers. The possible values are <syntaxhighlight lang='lua' inline>'village', 'leader', 'explicit', 'threat', 'battle aid', 'mass', 'support'</syntaxhighlight>. (Integer types still work, for now.)
  
 +
'''Notes:'''
 +
* This code is parsed by the Lua engine and the targets are available to any engine.
 +
* Targets can be read in Lua using '''ai.get_targets()'''
 +
** This produces a Lua array like the one above, with one element for each hex that was found as a target (even if SUFs or SLFs were used in WML).
 +
** {{DevFeature1.13|5}} The array now contains string target types, not integer target types. Thus, any previous use of get_targets() which checks the type must be updated.
 +
** The table also includes all the [[RCA_AI#RCA_AI_Aspect_and_Goal_Configuration|automatically generated targets]].
 +
* {{DevFeature1.13|5}} A Lua goal may also include an [args] subtag, which is passed to the goal evaluation function in the same way as for candidate actions.
  
== Persistence ==
+
== Lua Candidate Actions ==
  
If you need to store some data between save/load routines, you probably need a place to store the data. For this purpose, a field named "data" is automatically added to the return value of your engine code. The save routine will find this field, convert it to a config and store its content in readable format. When the scenario gets loaded back, the engine code will inject the data back in the AI context. All other data will be lost. Note that all content of "data" needs to be in WML table format, otherwise it will not be saved.
+
Lua candidate actions are the recommended method for creating custom AIs. They are described on a [[Creating_Custom_AIs#Setting_Up_a_Custom_AI|separate page]].
  
 +
== Lua AI — Legacy Methods ==
 +
 +
The recommended method of using custom Lua AI functionality is described at [[Creating Custom AIs]].  A variety of alternative older methods also still exist.  There should be few, if any, situations in which using these methods is advantageous, but for completeness they are described on a [[Lua_AI_Legacy_Methods_Howto|separate page]].
 +
 +
The methods include:
 +
 +
* '''Lua stage''': There should not be anything that can be done with the Lua stage that cannot just as easily be done with the ''main_loop'' stage.
 +
* '''Lua engine''': With the new type of Lua candidate actions, it is not necessary any more to define a Lua engine.  This is only needed for the old-style CAs.
 +
* The '''old syntax''' for defining '''Lua candidate actions''':  There is no advantage in using the old syntax over the new one. Since the evaluation and execution code was given as separate `evaluation=` and `execution=` keys, there is no chance of shared state between the two.
 +
* '''Preload event''': It is not necessary to set up a preload event any more (the one possible exception might be in order to set up a debug testing mechanism).
 +
* '''Behavior (sticky) candidate actions''': These are Lua CAs which attach directly to specific units.  They could, in principle, still be useful.  However, using the new syntax for Lua CAs with a condition to filter for specific units is usually done just as easily.  Several of the [[Micro AIs]] demonstrate how to do so.
  
 
== Debug access to the AI tree ==
 
== Debug access to the AI tree ==
  
  wesnoth.debug_ai(side)
+
It is now possible to access candidate actions (and some other AI components) and have them updated automatically without requiring reloading. Only external candidate actions work in this way though. Candidate actions defined in the engine inside the config file of the scenario will not be updated if their code is directly in the [engine] tag, and they will only be updated on reloading if they are set up by including a file.
-- has been added. The function only works in debug mode and returns a table containing the component tree of the active AI for a side, the engine functions and the accessor to the ai.* table of the LuaAI  Also allows the user to execute stages and evaluate/execute candidate actions(eval and exec can be run independently).
+
 
 +
The purpose of this is to aid with debugging. Now the developer does not have to reload the game to test the changes done in the candidate action code.
  
Structure of the table(example):
+
Access to the AI tree is done with function
 +
wesnoth.debug_ai(side)
 +
The function only works in debug mode '''and''' if Wesnoth is launched with the <code>--debug-lua</code> launch argument.  It returns a table containing the component tree of the active AI for a side, the engine functions and to the [[#Lua_AI_Functions|''ai'' table]] of the Lua AI.  It also allows the user to execute stages and to evaluate and/or execute candidate actions.
  
 +
Structure of the table (example showing only some of the output):
 
  {
 
  {
 
     stage_hello = function: 0x6dc24c0,
 
     stage_hello = function: 0x6dc24c0,
 
     ai = #the ai table,
 
     ai = #the ai table,
 
     data = {
 
     data = {
                },
+
    },
 
     do_moves = function: 0x6dc2590,
 
     do_moves = function: 0x6dc2590,
 
     candidate_action_evaluation_hello = function: 0x6dc2560,
 
     candidate_action_evaluation_hello = function: 0x6dc2560,
 
     candidate_action_evaluation_hello2 = function: 0x6dc2640,
 
     candidate_action_evaluation_hello2 = function: 0x6dc2640,
 
     components = {
 
     components = {
                          id = "",
+
        id = "",
                          stage = {
+
        stage = {
                                          another_stage = {
+
            another_stage = {
                                                                  name = "another_stage",
+
                name = "another_stage",
                                                                  id = "",
+
                id = "",
                                                                  exec = function: 0x17cd3bb,
+
                exec = function: 0x17cd3bb,
                                                                  engine = "lua",
+
                engine = "lua",
                                                                  stg_ptr = "userdata: 0x7c5a8d0"
+
                stg_ptr = "userdata: 0x7c5a8d0"
                                                              },
+
            },
                                          testing_ai_default::candidate_action_evaluation_loop = {
+
            testing_ai_default::candidate_action_evaluation_loop = {
                                                                                                          name = "testing_ai_default::candidate_action_evaluation_loop",
+
                name = "testing_ai_default::candidate_action_evaluation_loop",
                                                                                                        candidate_action = {
+
                candidate_action = {
                                                                                                                                  external = {
+
                    external = {
                                                                                                                                                      name = "external",
+
                        name = "external",
                                                                                                                                                      id = "",
+
                        id = "",
                                                                                                                                                      exec = function: 0x17cd2f3,
+
                        exec = function: 0x17cd2f3,
                                                                                                                                                      engine = "lua",
+
                        engine = "lua",
                                                                                                                                                      ca_ptr = "userdata: 0x6fb3600"
+
                        ca_ptr = "userdata: 0x6fb3600"
                                                                                                                                                  },
 
                                                                                                                                  second = {
 
                                                                                                                                                    name = "second",
 
                                                                                                                                                    id = "",
 
                                                                                                                                                    exec = function: 0x17cd2f3,
 
                                                                                                                                                    engine = "lua",
 
                                                                                                                                                    ca_ptr = "userdata: 0x6fb2f50"
 
                                                                                                                                                }
 
                                                                                                                              },
 
                                                                                                        id = "ca_loop",
 
                                                                                                        exec = function: 0x17cd3bb,
 
                                                                                                        engine = "",
 
                                                                                                        stg_ptr = "userdata: 0x6fb2728"
 
                                                                                                    }
 
                                    },
 
                        engine = "",
 
                        name = ""
 
 
                     },
 
                     },
    candidate_action_execution_hello2 = function: 0x6dc2670,
+
                    second = {
    candidate_action_execution_hello = function: 0x6dc2530
+
                        name = "second",
}
+
                        id = "",
 +
                        exec = function: 0x17cd2f3,
 +
                        engine = "lua",
 +
                        ca_ptr = "userdata: 0x6fb2f50"
 +
                    }
 +
                },
 +
                id = "ca_loop",
 +
                exec = function: 0x17cd3bb,
 +
                engine = "",
 +
                stg_ptr = "userdata: 0x6fb2728"
 +
            }
 +
        },
 +
        engine = "",
 +
        name = ""
 +
    },
 +
    candidate_action_execution_hello2 = function: 0x6dc2670,
 +
    candidate_action_execution_hello = function: 0x6dc2530
 +
}
  
 
The fields described as " name = function: 0xXXXXXXXX" are functions and can be called to using the usual function call syntax of Lua.
 
The fields described as " name = function: 0xXXXXXXXX" are functions and can be called to using the usual function call syntax of Lua.
wesnoth.debug_ai(2).ai -- will return the AI table of the Lua AI context.
 
  
wesnoth.debug_ai(2).stage[another_stage].exec() -- will execute the stage.
+
Some examples:
 +
<syntaxhighlight lang='lua'>
 +
wesnoth.debug_ai(2).ai -- returns the ai table containing of the Lua AI context
 +
 
 +
wesnoth.debug_ai(2).stage[another_stage].exec() -- executes the stage
 +
 
 +
wesnoth.debug_ai(2).ai.get_aggression() -- returns the current value of the aggression aspect of the AI
 +
</syntaxhighlight>
  
wesnoth.debug_ai(2).ai.get_aggression() -- will the return the current value of the aggression aspect of the AI
+
Therefore, we have up-to-date access to the whole AI, including the [[#Lua_AI_Variables_.E2.80.94_Persistence|''data'' table]].
  
Therefore, we have up-to-date access to the whole AI, including the data segment.
+
'''Notes:'''
 +
* Treat the structure above as an example only.  The real content and structure may vary. This should not be a problem for AI applications though, as this function is only used for debugging and will not work in non-debug mode.  It is really meant for testing only.
 +
* ''debug_utils'' from the [http://forum.wesnoth.org/viewtopic.php?f=58&t=28320&start=45 Wesnoth Lua Pack] can be used to display the content of essentially any Wesnoth Lua data structure, including the ''debug_ai'' tree.  You should always use some sort of vizualization like that (another, more basic option is to simply go through and print the keys of the returned table using the Lua ''pairs()'' function) when work with ''wesnoth.debug_ai''.
  
<b>NB!</b>: the back-end of this function will be refactored quite drastically and that may involve usage syntax changes. This shouldn't be a big problem, since this function is only used for debugging and won't work in non-debug mode. If something suddenly stops working for you, come back to this page and watch the updates.
 
  
 +
'''See also:''' [[Wesnoth AI]]
 
[[Category:AI]]
 
[[Category:AI]]
 
[[Category: Lua Reference|*]]
 
[[Category: Lua Reference|*]]

Revision as of 14:59, 30 November 2017

Wesnoth provides the functionality to both access and define all types of AI components using Lua. Instructions for using these components to customize the default AI and to create custom AIs are explained on other pages. See Wesnoth AI for a summary of the available documentation. This page is a reference guide to the Lua AI functions, variables and component syntax.

Lua AI Functions

The code of custom Lua candidate actions (CAs) has access to the ai table. This is a Lua table containing functions for getting information about the game state and for executing AI moves. Together with the other Wesnoth Lua functions they can be used in the CA evaluation and execution functions to code the desired AI behavior.

ai Table Functions Returning Game State Information

The ai table contains a large number of functions returning game state information. Some of those are simple scalar values which are self-explanatory, while others can be tables with many elements and complex structure. We will not show the structure of all these tables here. If you need to know the exact structure of such a table, use the method described below for accessing the AI tree.

Retrieving Aspect Values

The contents of many of the AI aspects can be read using ai table commands of the form

  • ai.get_<aspect_name>(), for example
    • ai.get_aggression()
    • ai.get_caution()

Note that this returns the result of the aspect evaluation given the game state at the time of the last invalidation of the aspect, not its definition. For example, ai.get_avoid() returns the list of avoided hexes, rather than the Standard Location Filter used to find them, and dynamic Lua aspects return the function evaluation result, not the function itself.

The full list of functions returning aspect values is

Functions returning scalar values (numbers, strings or booleans):
ai.get_aggression()
ai.get_attack_depth()
ai.get_caution()
ai.get_grouping()
ai.get_leader_aggression()
ai.get_leader_ignores_keep()
ai.get_leader_value()
ai.get_number_of_possible_recruits_to_force_recruit()
ai.get_passive_leader()
ai.get_passive_leader_shares_keep()
ai.get_recruitment_ignore_bad_combat()
ai.get_recruitment_ignore_bad_movement()
ai.get_scout_village_targeting()
ai.get_simple_targeting()
ai.get_support_villages()
ai.get_village_value()
ai.get_villages_per_scout()

Functions returning tables:
ai.get_attacks()
ai.get_avoid()
ai.get_leader_goal()
ai.get_recruitment_pattern()

(Version 1.13.5 and later only)

These functions are now deprecated. The new standard way of accessing aspect values is through the ai.aspects table, for example ai.aspects.aggression. All aspects can be accessed this way, including some that don't have function forms, such as the advancements aspect. (Version 1.13.6 and later only) The corresponding get_aspect_name functions are now deprecated, with the exception of ai.get_attacks() which returns an analysis of all possible attacks; this differs from ai.aspects.attacks which only returns the allowed units for use in attacks (in "own" and "enemy" subtables).

Functions Returning Other Game State Information

  • ai.get_targets(): Get the AI targets. See below for details.
  • ai.side: side number of the AI
  • ai.suitable_keep(unit): This returns the location of the closest keep to the unit passed as argument. Note that this returns the x and y coordinates of the keep as two separate arguments, so it needs to be called as local x, y = ai.suitable_keep(unit).

Finally, the following functions related to move maps (tables that relate the current positions of units and the hexes they can move to) also exist:

  • ai.get_new_dst_src()
  • ai.get_new_enemy_dst_src()
  • ai.get_new_enemy_src_dst()
  • ai.get_new_src_dst()
  • ai.is_dst_src_valid()
  • ai.is_enemy_dst_src_valid()
  • ai.is_enemy_src_dst_valid()
  • ai.is_src_dst_valid()
  • ai.recalculate_enemy_move_maps()
  • ai.recalculate_move_maps()

If you need to know what they do and the exact structure of their return values, use the method described below for accessing the AI tree.

The file ai/lua/stdlib.lua replaces the move-map functions with the following simplified set:

  • ai.get_dst_src()
  • ai.get_src_dst()
  • ai.get_enemy_dst_src()
  • ai.get_enemy_src_dst()

Note: The file stdlib.lua is loaded by default, but if a custom Lua [engine] tag is defined, it must load the file manually with the following code:

local ai_stdlib = wesnoth.require('ai/lua/stdlib.lua')
ai_stdlib.init(ai)

ai Table Functions for Executing AI Moves

For all functions below, units (unit, attacker, defender) can be passed either as a unit object in a single variable, or as two parameters specifying the unit's x and y coordinates. For functions taking more than one unit as arguments, it can also be a mix of the two.

  • ai.attack(attacker, defender, weapon, aggression): Execute attack by attacker against defender. If weapon is provided, it is the number of the weapon to be used (count starts at 1, not 0, as always in Lua), otherwise choice of the weapon is left to the AI engine. If aggression is provided, it is used to influence the choice of the best weapon. Obviously, this only makes sense if this choice is left to the engine, that is, if weapon is set to either 'nil' or '-1'.
  • ai.move(unit, to_x, to_y): Execute partial move of unit to hex (to_x, to_y). Partial move means that the unit keeps whatever movement points it has left after the move.
  • ai.move_full(unit, to_x, to_y): Execute full move of unit to hex (to_x, to_y). Full move means that the unit's movement points are set to zero at the end of the move.
  • ai.recall(unit_id, x, y): Recall the unit with id unit_id. An optional recruit location can be given in x and y. If the location is omitted, a suitable hex is automatically chosen by the AI engine.
  • ai.recruit(unit_type, x, y): Recruit a unit of type unit_type. An optional recruit location can be given in x and y. If the location is omitted, a suitable hex is automatically chosen by the AI engine.
  • ai.stopunit_attacks(unit): Remove remaining attacks from unit. This is equivalent to setting attacks_left=0.
  • ai.stopunit_moves(unit): Remove remaining movement points from unit. This is equivalent to setting moves=0.
  • ai.stopunit_all(unit): Remove both remaining attacks and remaining movement points from unit.
  • ai.synced_command(command, x1, y1): Execute the specified Lua command in a replay and MP safe way. command is a string containing the Lua code and (x1,y1) is an optional map location that is the only allowed external variable used in the code string (specified as Lua variables x1 and y1).

(Version 1.13.5 and later only) The above functions change the gamestate and thus are not always available. To be more specific, they are only available if ai.read_only is false. If it is true, the functions do not exist. This is the case everywhere except stage and candidate evaluation functions. The following functions are available for checking whether a planned AI move is possible. This is useful in order to prevent an aborted execution of a move, which might result in some or all of the remaining moves to be aborted by the AI due to a blacklisted candidate action.

  • ai.check_attack()
  • ai.check_move()
  • ai.check_recall()
  • ai.check_recruit()
  • ai.check_stopunit()
  • ai.check_synced_command()

These check functions take the same arguments as the respective AI action functions. They return a Lua table which looks something like the following for a successful check

{
    gamestate_changed = false,
    ok = true,
    status = 0,
    result = 'action_result::AI_ACTION_SUCCESS'
}

and like this for an unsuccessful check

{
    gamestate_changed = false,
    ok = false,
    status = 2008,
    result = 'move_result::E_NO_ROUTE'
}

Notes:

  • status is the error code returned by the AI engine in case of an unsuccessful code. The error string for a given error code can be found in file src/ai/actions.hpp. (Version 1.13.5 and later only) It is now also available as the result element of the table.
  • The move execution functions return the same table, with the only difference being that gamestate_changed is 'true' in case the execution resulted in a change of the game state (even if the move was only partially successful).

Lua AI Variables — Persistence

When Creating Custom AIs in Lua, it is sometimes necessary to transfer information between the evaluation and execution functions of the same candidate action (CA) or between CAs. It might also be necessary to have this information persist from turn to turn and across save/load cycles. This could, in principle, be done using WML variables, but depending on the type and amount of information, that could be awkward and/or inconvenient to do in practice.

The Wesnoth AI therefore includes a field called data which is automatically added to the internal AI context. The evaluation and execution functions of Lua candidate actions have access to this field if the third (optional) variable is passed to them. Assuming this variable is called self, the field is accessed as self.data.

When saving a game, the save routine finds this field, converts it to a config and stores its content in readable format in the save file. When the scenario gets loaded back, the engine code injects the data back into the AI context. Note that, in order for this to work, all content of data needs to be in WML table format. Other format content can also be used during an AI turn, for example to pass it between the evaluation and execution functions of the same CA, but it is lost when saving the game.

Dynamic Lua Aspects

It is possible to define aspects using Lua. These can be either static or dynamic, that is, their value can change depending on the game state. We demonstrate this here with a few examples.

First, as simple example of defining the aggression aspect statically in Lua:

[aspect]
    id="aggression"
    engine="lua"
    value="0.765"
[/aspect]

There is, of course, no advantage of this over simply defining the aspect in WML, we simply show it for completeness and for the syntax. An example of defining aggression dynamically so that it increases slowly from turn to turn is

[aspect]
    id=aggression
    engine=lua
	     
    code=<<
        local value = wesnoth.current.turn / 10.
        return value
    >>      
[/aspect]

Note the difference between the value and code attributes in the two examples.

(Version 1.13.5 and later only) A Lua aspect may also contain an [args] subtag, which is passed to the aspect evaluation function in the same way as for candidate actions.

The advancements Aspect

A slightly more elaborate example, using the advancements aspect, is given below. This aspect is different from others in that it can be used with a function advance(x, y), where (x, y) is the current location of the unit about to advance. You can then use wesnoth.get_unit(x, y) to retrieve the unit object itself. For example, the following code advances the unit with id 'Bob' to a swordsman (assuming here that this unit is a spearman), while all other spearmen are advanced to javelineers.

[ai]
    [aspect]
        id=advancements
        engine=lua

        code=<<
            function advance(x, y)
                local unit = wesnoth.get_unit(x, y)
                if (unit.id == 'Bob') then
                    return 'Swordsman'
                else
                    return 'Javelineer'
                end
            end
            return advance
        >>
    [/aspect]
[/ai]

Note that this aspect only affects AI units while they are attacking. When a defending unit advances, it is still decided randomly. That is because Wesnoth's design does not allow the AI to make out of turn decisions.

The attacks Aspect

(Version 1.13.5 and later only)

The attacks aspect may also be defined using the Lua engine; this is a little more complicated than most aspects, though. The aspect must return a table containing two keys, own and enemy. The value of each of these keys can be either a table defining a valid WML unit filter or a function which, when called with a unit proxy as its argument, returns true if the unit may be attacked and false otherwise.

Lua Goals and Targets

It is also possible to define AI goals and targets in Lua. This is, however, somewhat inconvenient and we generally recommend using the [goal] tag with WML Standard Unit Filters (SUFs) or Standard Location Filters (SLFs) instead. That is much more versatile and it makes no difference to the AI behavior whether the goals are defined using WML or Lua.

The only time that defining goals in Lua might be advantageous is if one wants to use goals with dynamic values, such as in this example, where one goal has a constant value, while the other's value increases from turn to turn; or if one wants to define other types of targets that the other goals do not support (not demonstrated here).

[goal]
    name=lua_goal
    engine=lua
    code = <<
        local turn = wesnoth.current.turn
        local targets = {}
        targets[1] = { value = 2, type = 2, loc = { x = 5, y = 6 } }
        targets[2] = { value = turn * 2 , type = 2, loc = { x = 4, y = 9 } }
        return targets
    >>
[/goal]

As you can see, the return value must be an array containing the targets. Each target must contain three fields:

  • value: The value of the target, equivalent to that defined for WML goals
  • loc: A table with attributes x and y defining the target's location. This is one of the disadvantages with respect to the WML syntax. Each goal needs to be defined individually by its coordinates. The use of SUFs and SLFs is not possible.
  • type: A number defining the type of the target. File src/ai/default/contexts.hpp defines the available types as
enum TYPE { VILLAGE, LEADER, EXPLICIT, THREAT, BATTLE_AID, MASS, SUPPORT };

User-defined targets should usually be given type = 2 for target and target_location type targets, and type = 3 for protect_* type targets. This corresponds to EXPLICIT and THREAT targets (the third and fourth in the enum list, with counting starting at 0). (Version 1.13.5 and later only) Target types can now be specified as strings instead of integers. The possible values are 'village', 'leader', 'explicit', 'threat', 'battle aid', 'mass', 'support'. (Integer types still work, for now.)

Notes:

  • This code is parsed by the Lua engine and the targets are available to any engine.
  • Targets can be read in Lua using ai.get_targets()
    • This produces a Lua array like the one above, with one element for each hex that was found as a target (even if SUFs or SLFs were used in WML).
    • (Version 1.13.5 and later only) The array now contains string target types, not integer target types. Thus, any previous use of get_targets() which checks the type must be updated.
    • The table also includes all the automatically generated targets.
  • (Version 1.13.5 and later only) A Lua goal may also include an [args] subtag, which is passed to the goal evaluation function in the same way as for candidate actions.

Lua Candidate Actions

Lua candidate actions are the recommended method for creating custom AIs. They are described on a separate page.

Lua AI — Legacy Methods

The recommended method of using custom Lua AI functionality is described at Creating Custom AIs. A variety of alternative older methods also still exist. There should be few, if any, situations in which using these methods is advantageous, but for completeness they are described on a separate page.

The methods include:

  • Lua stage: There should not be anything that can be done with the Lua stage that cannot just as easily be done with the main_loop stage.
  • Lua engine: With the new type of Lua candidate actions, it is not necessary any more to define a Lua engine. This is only needed for the old-style CAs.
  • The old syntax for defining Lua candidate actions: There is no advantage in using the old syntax over the new one. Since the evaluation and execution code was given as separate `evaluation=` and `execution=` keys, there is no chance of shared state between the two.
  • Preload event: It is not necessary to set up a preload event any more (the one possible exception might be in order to set up a debug testing mechanism).
  • Behavior (sticky) candidate actions: These are Lua CAs which attach directly to specific units. They could, in principle, still be useful. However, using the new syntax for Lua CAs with a condition to filter for specific units is usually done just as easily. Several of the Micro AIs demonstrate how to do so.

Debug access to the AI tree

It is now possible to access candidate actions (and some other AI components) and have them updated automatically without requiring reloading. Only external candidate actions work in this way though. Candidate actions defined in the engine inside the config file of the scenario will not be updated if their code is directly in the [engine] tag, and they will only be updated on reloading if they are set up by including a file.

The purpose of this is to aid with debugging. Now the developer does not have to reload the game to test the changes done in the candidate action code.

Access to the AI tree is done with function

wesnoth.debug_ai(side)

The function only works in debug mode and if Wesnoth is launched with the --debug-lua launch argument. It returns a table containing the component tree of the active AI for a side, the engine functions and to the ai table of the Lua AI. It also allows the user to execute stages and to evaluate and/or execute candidate actions.

Structure of the table (example showing only some of the output):

{
    stage_hello = function: 0x6dc24c0,
    ai = #the ai table,
    data = {
    },
    do_moves = function: 0x6dc2590,
    candidate_action_evaluation_hello = function: 0x6dc2560,
    candidate_action_evaluation_hello2 = function: 0x6dc2640,
    components = {
        id = "",
        stage = {
            another_stage = {
                name = "another_stage",
                id = "",
                exec = function: 0x17cd3bb,
                engine = "lua",
                stg_ptr = "userdata: 0x7c5a8d0"
            },
            testing_ai_default::candidate_action_evaluation_loop = {
                name = "testing_ai_default::candidate_action_evaluation_loop",
                candidate_action = {
                    external = {
                        name = "external",
                        id = "",
                        exec = function: 0x17cd2f3,
                        engine = "lua",
                        ca_ptr = "userdata: 0x6fb3600"
                    },
                    second = {
                        name = "second",
                        id = "",
                        exec = function: 0x17cd2f3,
                        engine = "lua",
                        ca_ptr = "userdata: 0x6fb2f50"
                    }
                },
                id = "ca_loop",
                exec = function: 0x17cd3bb,
                engine = "",
                stg_ptr = "userdata: 0x6fb2728"
            }
        },
        engine = "",
        name = ""
    },
    candidate_action_execution_hello2 = function: 0x6dc2670,
    candidate_action_execution_hello = function: 0x6dc2530
}

The fields described as " name = function: 0xXXXXXXXX" are functions and can be called to using the usual function call syntax of Lua.

Some examples:

wesnoth.debug_ai(2).ai -- returns the ai table containing of the Lua AI context

wesnoth.debug_ai(2).stage[another_stage].exec() -- executes the stage

wesnoth.debug_ai(2).ai.get_aggression() -- returns the current value of the aggression aspect of the AI

Therefore, we have up-to-date access to the whole AI, including the data table.

Notes:

  • Treat the structure above as an example only. The real content and structure may vary. This should not be a problem for AI applications though, as this function is only used for debugging and will not work in non-debug mode. It is really meant for testing only.
  • debug_utils from the Wesnoth Lua Pack can be used to display the content of essentially any Wesnoth Lua data structure, including the debug_ai tree. You should always use some sort of vizualization like that (another, more basic option is to simply go through and print the keys of the returned table using the Lua pairs() function) when work with wesnoth.debug_ai.


See also: Wesnoth AI