Difference between revisions of "FormulaAI Functions"

From The Battle for Wesnoth Wiki
m ('timeofday modifier' function {{DevFeature}})
m (Fix broken page formatting)
 
(21 intermediate revisions by 8 users not shown)
Line 1: Line 1:
 +
{| class="wikitable" style="text-align: center;"
 +
|'''Warning'''
 +
|-
 +
|Formula AI is still functional for the time being, but it is not maintained any more.
 +
That applies to both the code and these wiki pages.
 +
|}
 +
 +
See also: [[Formula_AI_Howto]]
 +
 
= Overview =
 
= Overview =
  
Line 15: Line 24:
 
Also function may return only single argument, or be able to return a whole list or a map.
 
Also function may return only single argument, or be able to return a whole list or a map.
  
There are a wide variety of functions which can be used to accomplish many different tasks. You can also [http://www.wesnoth.org/wiki/FormulaAI#Custom_Functions define your own functions].
+
There are a wide variety of functions which can be used to accomplish many different tasks. You can also [http://www.wesnoth.org/wiki/Wesnoth Formula Language#Functions define your own functions].
 
 
=core functions=
 
 
 
=== 'abs' function ===
 
 
 
<number> = abs( <input number> )
 
 
 
Function returns absolute value of an <input number>, for example
 
 
 
abs( -5 )
 
 
 
will return 5.
 
 
 
=== 'choose' function ===
 
 
 
<result> = choose( <input list> , [ <string> ,] <formula> )
 
 
 
This function evaluates <formula> for each item in the <input> (which can be a list ro a map). Will evaluate to the one item which <formula> gave the highest value.  For example:
 
 
 
choose(my_units, level)
 
 
 
gives back the unit with the highest level.
 
 
 
'''Note:''' The implicit input when evaluating a mapping/filtering function's <formula> component will be that specific item under evaluation (in this example one of "my_units"), and it can be explicitly referenced as 'self' when necessary. Optional <string> paremater indicates what word used in <formula> is equivalent to 'self'.
 
 
 
When evaluating the map data type, we can reference to each key by 'key' and each value by 'value'. For example:
 
 
 
choose( [ 'elf' -> 10, 'dwarf' -> 20 ], value )
 
 
 
Will return a key-value pair
 
 
 
{ key->'dwarf', value->20 }
 
 
 
=== 'contains_string' function ===
 
 
 
<boolean> = contains_string( <string>, <key> )
 
 
 
Returns 1 if <key> can be found withing <string>, 0 otherwise
 
 
 
contains_string( 'Testing', 'ing' )
 
 
 
returns
 
 
 
1
 
 
 
=== 'debug_print' function ===
 
 
 
{{DevFeature}} '''you need to enable formula log (--log-info=formula_ai) to see the result of this call'''
 
 
 
<formula> = debug_print( [ <optional string> ,] <formula>  )
 
 
 
This function can be used for debging the formulas. It takes formula, writes output to the console and return it unchanged. For example:
 
 
 
debug_print( [ 1, 2, 3 ] )
 
 
 
will result in printing to the console
 
 
 
[ 1, 2, 3 ]
 
 
 
Return value is the same.
 
 
 
We can specify optional parameter that helps to distinguish what each of debug_print outputs is (useful if we have multiple debug_print functions):
 
 
 
debug_print( 'My array: ', [ 1, 2, 3 ] )
 
 
 
will write in the console:
 
 
 
My array: [ 1, 2, 3 ]
 
 
 
And return
 
 
 
[ 1, 2, 3 ]
 
 
 
=== 'debug_float' function {{DevFeature}} ===
 
 
 
<formula> = debug_float( <location>, [ <optional string> ,] <formula>  )
 
 
 
This function can be used for debging the formulas. It takes formula, floats a label containing the output on the hex specified (in the same way damage is displayed)  and return it unchanged. For example:
 
 
 
debug_float(me.loc, me.id )
 
 
 
will make a label containing the id of the unit "me" float over the unit
 
 
 
 
 
Return value is also the unit id.
 
 
 
We can specify optional parameter that helps to distinguish what each of debug_print outputs is (useful if we have multiple debug_print functions):
 
 
 
debug_float( me.loc, 'id: ', me.id )
 
 
 
will make the following label
 
 
 
id: <unit id
 
 
 
And return the unit id
 
 
 
=== 'dir' function ===
 
 
 
<list of names> = dir ( <input object> )
 
 
 
This function return list with all names of <input object's> members. For example:
 
 
 
dir( my_leader )
 
 
 
will result in output:
 
 
 
[ 'x', 'y', 'loc', 'id', 'leader', 'hitpoints', 'max_hitpoints', 'experience', 'max_experience', 'level',
 
'total_movement', 'movement_left', 'side', 'is_enemy', 'is_mine']
 
 
 
This command is useful in formula command line, to get information about members of different type of data. To get list of members of the ai, type:
 
 
 
dir( self )
 
 
 
=== 'filter' function ===
 
 
 
<result> = filter( <input>, [ <string> ,] <formula> )
 
 
 
This function will run <formula> on each item in the <input> (which can be a list or a map). Will evaluate to a <result> which only contains items the <formula> was true for. Optional <string> indicates what word used in <formula> is equivalent to 'self'. For example:
 
 
 
filter(my_units, hitpoints < max_hitpoints)
 
 
 
will return all of your units which have less than maximum hitpoints. For instance this could be used if looking for candidates for healing.
 
 
 
=== 'find' function ===
 
 
<result> = find( <input>, [ <string>,] <formula> )
 
 
 
This function will run <formula> on each item in the <input> (which can be a list or a map) and will return a first item for which <formula> was true. Optional <string> indicates what word used in <formula> is equivalent to 'self'. For example:
 
 
 
filter(units, id = 'Elvish Archer' )
 
 
 
will return first unit with id equal to 'Elvish Archer'.
 
 
 
=== 'head' function ===
 
 
 
<variable> = head( <list of variables> )
 
 
 
Head returns first item from the <list of variables>, for example
 
 
 
head( [ 5, 7, 9] )            #returns 5
 
head( [ 'Orc', 'Human' ] )    #returns 'Orc'
 
 
 
=== 'if' function ===
 
 
 
<result> = if( <condition> , <if true> [, <condition 2>, <if true 2>, ... ] [, <otherwise> ] )
 
 
 
 
 
If the <condition> parameter is true, the function will evaluate to being equal to its second input ( <if true> ), otherwise, if third imput is specified  ( <otherwise> ), it will evaluate to being equal to it, else it will evaluate to null.
 
For instance, an AI that recruits Wolf Riders on the first turn, and Orcish Grunts thereafter might look like this:
 
 
 
move="if(turn = 1, recruit('Wolf Rider'), recruit('Orcish Grunt'))"
 
 
 
You can specify more than one pair of <condition> and <if true> paramters, for example such formulas:
 
 
 
if( var = 'first', 1, var = 'second', 2, var = 'third', 3 )
 
if( var = 'first', 1, var = 'second', 2, var = 'third', 3, 100 )
 
 
 
In case that var is equal to 'second' above will evaluate to
 
 
 
2
 
2
 
 
 
And in case of var equal to 'fifth':
 
 
 
null
 
100
 
 
 
=== 'index_of' function ===
 
 
 
<result> = index_of( <value>,<list> )
 
 
 
This function will return the first index where <value> can be found in <list>
 
 
 
It will return -1 if the value is not found
 
 
 
=== 'keys' function ===
 
 
 
<result list> = keys( <input map> )
 
 
 
Extract key values from a <input map> and return them as a <result list>
 
 
 
keys( [ 'Elvish Fighter' -> 50, 'Elvish Archer' -> 60 ] )
 
 
 
Returns
 
 
 
[ 'Elvish Fighter', 'Elvish Archer' ]
 
 
 
=== 'map' function ===
 
 
 
<result> = map( <input> , [ <string> ,] <formula> )
 
 
 
This function will run <formula> on each item in the <input> (which can be a list or a map), and evaluate to a new <result> list, or a map, which contains the same number of items as in <input>, with the formulas run on each item. Optional <string> indicates what word used in <formula> is equivalent to 'self'. For example:
 
 
 
map( [10,20], self*self)
 
 
 
and
 
 
 
map( [10,20], 'value', value*value)
 
 
 
both will result in [100, 400]. Formula:
 
  
map(my_units, hitpoints)
+
In addition to the functions listed here, there are several [[Wesnoth Formula Language#Core Functions|core functions]] available.
  
will give a list back with the number of hitpoints each unit has. This is more useful in conjunction with other functions.
+
= Base functions =
  
map( [ 'elf' -> 10, 'dwarf' -> 20 ], value*2 )
+
== 'attack' function ==
 
 
Above will produce  [ 'elf' -> 20, 'dwarf' -> 40 ]. Note that in case of a map data type, 'map' function can modify only the value.
 
 
 
map( tomap([3,5,8,8]), value+key*100 )
 
 
 
Above will produce [3 -> 301, 5 -> 502, 8 -> 801]. This can be used to take a list and make a map containing pairs [element_from_that_list -> f(element_from_that_list,number_of_repetitions_of_that_element_in_that_list) ] where f is an arbitrary function.
 
 
 
=== 'max' function ===
 
 
 
<number> = max( <list of numbers> )
 
 
 
Function will return maximal number from a list,
 
 
 
max( [ 2, 8, -10, 3] )
 
 
 
will return 8.
 
 
 
=== 'min' function ===
 
 
 
<number> = min( <list of numbers> )
 
 
 
Function will return minimal number from a list,
 
 
 
min( [ 3, 7, -2, 6] )
 
 
 
will return -2.
 
 
 
 
 
 
 
=== 'size' function ===
 
 
 
<number> = size( <list of variables> )
 
 
 
This function returns how many variables are stored in a list:
 
 
 
size( [ 5, 7, 9] )                #return 3
 
size( [ 'Archer', 'Fighter' ] )  #return 2
 
 
 
=== 'sort' function ===
 
 
 
<result list> = sort( <input list> , <formula> )
 
 
 
This function evaluates to a <result list> sorted according to the comparison <formula> for each item 'a' and its successor 'b'. For instance, sorting units according to hitpoints would be done by:
 
 
 
sort( my_units, a.hitpoints > b.hitpoints )
 
 
 
=== 'sum' function ===
 
 
 
<number> = sum( <list of numbers> )
 
 
 
This function evaluates to the sum of the items in the <list of numbers>. For example
 
 
 
sum( [ 2, 5, 8] )
 
 
 
returns 15, and:
 
 
 
sum( map( my_units,  max_hitpoints - hitpoints ) )
 
 
 
finds the total damage your units have taken.
 
 
 
=== 'switch' function ===
 
 
 
<result> = switch( <variable>, <value 1>, <outcome 1>, ... , <value N>, <outcome N> [, <default outcome> ] >
 
 
 
Switch funtion takes variable, and checks if it is equal to any of the specified <values>. If matching value is found, <outcome> assigned to it is returned, if not, then function returns either <default outcome> (if specified) or null.
 
 
 
=== 'tolist' function ===
 
 
 
<list> = tolist( <input map> )
 
 
 
This function takes map and return a list of key-value pairs objects. For example:
 
 
 
tolist( [ 'Elf' -> 10, 'Dwarf' -> 20] )
 
 
 
will return:
 
 
 
[{key->'Elf',value->10}, {key->'Dwarf',value->20}]
 
 
 
=== 'tomap' function ===
 
 
 
<map> = tomap( <input list A> [, <input list B> ] )
 
 
 
This function takes one or two lists as input and returns a map. If only one list is specified, then function will evaluate this list, count how many simmilar elements are withing this list, and return a map with keys being these elements, and values being a number representing of them list contains, For example:
 
 
 
tomap( ['elf', 'dwarf', 'elf', 'elf', 'human', 'human' ] )
 
 
 
will return:
 
 
 
[ 'elf' -> 3, 'dwarf' -> 1, 'human' -> 2 ]
 
 
 
If two lists are specified, then elements of the first one will be used as a keys, and elements of second one as a values, when creating a output map. Note that these input lists must be of the same length.
 
 
 
tomap( [ 'elf', 'dwarf' ], [10, 20] )
 
 
 
will result in:
 
 
 
[ 'elf' -> 10, 'dwarf' -> 20 ]
 
 
 
=== 'values' function ===
 
 
 
<result list> = values( <input map> )
 
 
 
Extract values assigned to keys from a <input map> and return them as a <result list>
 
 
 
values( [ 'Elvish Fighter' -> 50, 'Elvish Archer' -> 60 ] )
 
 
 
Returns
 
 
 
[ 50, 60 ]
 
 
 
=== 'wave' function ===
 
 
 
<value> = wave( <value> )
 
 
 
given a value V, returns
 
 
 
  sin(2*pi/(V%1000/1000) )
 
 
 
= AI specific functions =
 
 
 
== Base functions ==
 
 
 
=== 'attack' function ===
 
  
 
  <action> = attack( <attacker's position>, <destination>, <attack location> [,  <weapon> ] )
 
  <action> = attack( <attacker's position>, <destination>, <attack location> [,  <weapon> ] )
Line 350: Line 36:
 
The first three parameters are locations. At the begining, unit which is standing at <attacker's position> is moved to <destination> place. Then, from that place unit is attacking unit which stands in place marked by <attack location>. Fourth optional parameter is number, and indicates which weapon attacker should use - if not specified, best possible weapon is chosed automatically.
 
The first three parameters are locations. At the begining, unit which is standing at <attacker's position> is moved to <destination> place. Then, from that place unit is attacking unit which stands in place marked by <attack location>. Fourth optional parameter is number, and indicates which weapon attacker should use - if not specified, best possible weapon is chosed automatically.
  
=== 'fallback' function ===
+
== 'fallback' function ==
  
 
  <action> = fallback( [<name>] )
 
  <action> = fallback( [<name>] )
Line 362: Line 48:
 
  fallback( 'human' )
 
  fallback( 'human' )
  
=== 'get_unit_type' function ===
+
== 'get_unit_type' function ==
  
 
  <unit> = get_unit_type( <unit name> )
 
  <unit> = get_unit_type( <unit name> )
Line 372: Line 58:
 
will result in returning unit_type of a Mage.
 
will result in returning unit_type of a Mage.
  
=== 'move' function ===
+
== 'move' function ==
  
  <action> = move( <source> , <destination> )
+
  <action> = move( <nowiki><source></nowiki> , <destination> )
  
Moves unit from <source> to <destination> and sets unit movement to 0. For example unit formula like:
+
Moves unit from <nowiki><source></nowiki> to <destination> and sets unit movement to 0. For example unit formula like:
  
 
  move(my_leader.loc, loc(my_leader.loc.x, my_leader.loc.y - 1) )
 
  move(my_leader.loc, loc(my_leader.loc.x, my_leader.loc.y - 1) )
Line 382: Line 68:
 
will make leader move one hex north. Leader's movement points will be reset to 0.
 
will make leader move one hex north. Leader's movement points will be reset to 0.
  
=== 'move_partial' function ===
+
== 'move_partial' function ==
  
  <action> = move_partial( <source> , <destination> )
+
  <action> = move_partial( <nowiki><source></nowiki> , <destination> )
  
Moves unit from <source> to <destination>. For example unit formula like:
+
Moves unit from <nowiki><source></nowiki> to <destination>. For example unit formula like:
  
 
  move(my_leader.loc, loc(my_leader.loc.x, my_leader.loc.y - 1) )
 
  move(my_leader.loc, loc(my_leader.loc.x, my_leader.loc.y - 1) )
Line 393: Line 79:
  
  
=== 'run_file' function {{DevFeature}} ===
+
== 'run_file' function ==
  
 
  <formula> = run_file( <string_file_location> )
 
  <formula> = run_file( <string_file_location> )
Line 402: Line 88:
 
At the moment, this function is useful mainly for debugging from the game console.
 
At the moment, this function is useful mainly for debugging from the game console.
  
=== 'set_unit_var' function ===
+
== 'set_unit_var' function ==
  
 
  <action> = set_unit_var( <key>, <value> , <location>)
 
  <action> = set_unit_var( <key>, <value> , <location>)
Line 409: Line 95:
  
  
=== 'set_var' function ===
+
== 'set_var' function ==
  
 
  <action> = set_var( <key> , <value> )
 
  <action> = set_var( <key> , <value> )
Line 419: Line 105:
 
Will create variable with name 'Number one' and assign 1 to it.
 
Will create variable with name 'Number one' and assign 1 to it.
  
== Evaluation ==
+
= Evaluation =
=== 'calculate_outcome' function ===
+
== 'calculate_outcome' function ==
  
  [<list of outcoms>]  = calculate_outcome(<attacker> ,<attacker location> , <defender location> , [<weapon>] )
+
  [<list of outcomes>]  = calculate_outcome(<attacker current location>, <attacker location> , <defender location> , [<weapon>] )
  
returns a list of possible outcomes when <attacker> attacks the unit at <defender location> from <attacker location>
+
returns a list of possible outcomes when unit standing at <attacker current location> attacks the unit at <defender location> from <attacker location>
  
 
if no weapon is provided, it will return for the weapon considered the best by the C++ weapon choice algorithm (the one used to select default weapon in normal games)
 
if no weapon is provided, it will return for the weapon considered the best by the C++ weapon choice algorithm (the one used to select default weapon in normal games)
  
=== 'chance to hit' function ===
+
== 'chance to hit' function ==
  
 
  <number> = chance_to_hit( <unit> , <location> )
 
  <number> = chance_to_hit( <unit> , <location> )
Line 439: Line 125:
  
  
=== 'evaluate_for_position' function ===
+
== 'evaluate_for_position' function ==
  
 
  <variant> = evaluate_for_position(<position>, <formula> )
 
  <variant> = evaluate_for_position(<position>, <formula> )
Line 445: Line 131:
 
Returns the result of <formula> as if <formula> was evaluated with a position of <position> instead of the current position
 
Returns the result of <formula> as if <formula> was evaluated with a position of <position> instead of the current position
  
=== 'max_possible_damage' function ===
+
== 'max_possible_damage' function ==
  
 
  <number> = max_possible_damage( <attacking unit> , <defending unit> )
 
  <number> = max_possible_damage( <attacking unit> , <defending unit> )
Line 451: Line 137:
 
Function returns highest possible damage that <attacking unit> can inflict to <defending unit>.
 
Function returns highest possible damage that <attacking unit> can inflict to <defending unit>.
  
=== 'max_possible_damage_with_retaliation' function ===
+
== 'max_possible_damage_with_retaliation' function ==
  
 
  <number> = max_possible_damage_with_retaliation( <attacking unit> , <defending unit> )
 
  <number> = max_possible_damage_with_retaliation( <attacking unit> , <defending unit> )
Line 461: Line 147:
 
in which first two elements are highest possible damage that <attacking unit> can inflict to <defending unit> with melee and ranged attacks, and latter two elements are highest possible damage that <defending unit> can inflict to <attacking unit> also with melee and ranged attacks.
 
in which first two elements are highest possible damage that <attacking unit> can inflict to <defending unit> with melee and ranged attacks, and latter two elements are highest possible damage that <defending unit> can inflict to <attacking unit> also with melee and ranged attacks.
  
=== 'movement_cost' function ===
+
== 'movement_cost' function ==
  
 
  <number> = movement_cost( <unit> , <location> )
 
  <number> = movement_cost( <unit> , <location> )
  
This function returns movememtn cost of given <unit> in a specific <location>. For example:
+
This function returns movement cost of given <unit> in a specific <location>. For example:
  
 
  movement_cost( my_leader , my_leader.loc )
 
  movement_cost( my_leader , my_leader.loc )
Line 471: Line 157:
 
shows what movement cost your leader has in a place he is currently standing on.
 
shows what movement cost your leader has in a place he is currently standing on.
  
=== 'outcomes' function ===
+
== 'next_hop' function ==
 +
  [<location>] = next_hop( src,dst, [unit_location])
 +
this function returns the 'next hop' location to move for the unit at unit_location standing at src if it wants to move to dst. if unit_location is omitted, it defaults to src.
 +
 
 +
== 'outcomes' function ==
 
   [<list of positions>] = outcomes( <attack> )
 
   [<list of positions>] = outcomes( <attack> )
  
Line 484: Line 174:
  
  
=== 'timeofday_modifier' function {{DevFeature}} ===
+
== 'timeofday_modifier' function ==
  
 
  <number> = timeofday_modifier(<unit> [, <location>] )
 
  <number> = timeofday_modifier(<unit> [, <location>] )
  
This function returns combat modifier due to time of day, taking alignment, illuminate, time of day and fearless trait into account. note: 'leadership' and 'slowed' are not taken into account.
+
This function returns combat modifier (percentage, such as 25 for lawful unit during the day) due to time of day, taking alignment, illuminate, time of day and fearless trait into account. note: 'leadership' and 'slowed' are not taken into account.
unit - unit for which to evaluate
+
 
location - location for which to evaluate (optional, defaults to unit's current location)
+
unit - unit for which to evaluate
 +
 
 +
location - location for which to evaluate (optional, defaults to unit's current location)
  
== Gamemap functions ==
+
= Gamemap functions =
  
  
=== 'adjacent_locs' function ===
+
== 'adjacent_locs' function ==
 
   
 
   
 
  <loc list> = adjacent_locs( <location> )
 
  <loc list> = adjacent_locs( <location> )
Line 502: Line 194:
  
  
=== 'castle_locs' function ===
+
== 'castle_locs' function ==
  
 
  [<loc list>] = castle_locs(<location>)
 
  [<loc list>] = castle_locs(<location>)
Line 509: Line 201:
 
given a hex, this function will return the list of all locations that is connected to <location> only by keeps or castle. I.e the hexes you can recruit from, assumin that you are at a keep at <location>
 
given a hex, this function will return the list of all locations that is connected to <location> only by keeps or castle. I.e the hexes you can recruit from, assumin that you are at a keep at <location>
  
=== 'close enemies' function ===
+
== 'close enemies' function ==
  
 
  <units list> = close_enemies( <location> , <distance> )
 
  <units list> = close_enemies( <location> , <distance> )
Line 519: Line 211:
 
gives back a list of enemies in the distance of 5 tiles or less from the tile (10, 10).
 
gives back a list of enemies in the distance of 5 tiles or less from the tile (10, 10).
  
=== 'distance_between' function ===
+
== 'distance_between' function ==
  
 
  <number> = distance_between( <location A> , <location B> )
 
  <number> = distance_between( <location A> , <location B> )
Line 529: Line 221:
 
will return 3.
 
will return 3.
  
=== 'distance_to_nearest_unowned_village' function ===
+
== 'distance_to_nearest_unowned_village' function ==
  
 
  <number> = distance_to_nearest_unowned_village( <location A> )
 
  <number> = distance_to_nearest_unowned_village( <location A> )
Line 535: Line 227:
 
This function returns distance (in hexes) between <location A> and nearest unowned village.
 
This function returns distance (in hexes) between <location A> and nearest unowned village.
  
=== 'defense_on' function ===
+
== 'defense_on' function ==
  
 
  <number> = defense_on( <unit> , <location> )
 
  <number> = defense_on( <unit> , <location> )
Line 545: Line 237:
 
shows how good defense your leader has in a place he is currently standing on.
 
shows how good defense your leader has in a place he is currently standing on.
  
=== 'find_shroud' function ===
+
== 'find_shroud' function ==
  
 
  <locations list> = find_shroud()
 
  <locations list> = find_shroud()
Line 551: Line 243:
 
This function will return a list of locations of shrouded hexes.
 
This function will return a list of locations of shrouded hexes.
  
=== 'is_unowned_village' function ===
+
 
 +
== 'is_avoided_location' function ==
 +
<boolean> = is_avoided_location(map_location)
 +
 
 +
This function will return the answer to question 'is location avoided by current ai?'
 +
 
 +
== 'is_unowned_village' function ==
  
 
  <boolean> = is_unowned_village( <map or ai.map> , <location> )  #1
 
  <boolean> = is_unowned_village( <map or ai.map> , <location> )  #1
Line 562: Line 260:
 
   
 
   
  
=== 'is_village' function ===
+
== 'is_village' function ==
  
 
  <boolean> = is_village( <map or ai.map> , <location> )  #1
 
  <boolean> = is_village( <map or ai.map> , <location> )  #1
Line 580: Line 278:
 
Remember, when using is_village in custom function, you either have to access map by writing 'ai.map', or specify ai as a 'default input'.
 
Remember, when using is_village in custom function, you either have to access map by writing 'ai.map', or specify ai as a 'default input'.
  
=== 'loc' function ===
+
== 'loc' function ==
  
 
  <location> = loc( <X number>, <Y number> )
 
  <location> = loc( <X number>, <Y number> )
Line 586: Line 284:
 
Function will return a location (pair of numbers) from two given input arguments.
 
Function will return a location (pair of numbers) from two given input arguments.
  
=== 'shortest_path' function ===
+
== 'shortest_path' function ==
  
 
  <list of locations> = shortest_path( <location A> , <location B> [, <unit location> ] )
 
  <list of locations> = shortest_path( <location A> , <location B> [, <unit location> ] )
Line 592: Line 290:
 
When only 2 parameters are specified, function returns list with all locations that unit standing on <location A> has to move through to get to <location B>. If optional 3rd parameter is specified, it returns list with all locations that unit standing on <unit location> would need to move through to get from <location A> to <location B>. This function takes into account zone of control of enemy units.
 
When only 2 parameters are specified, function returns list with all locations that unit standing on <location A> has to move through to get to <location B>. If optional 3rd parameter is specified, it returns list with all locations that unit standing on <unit location> would need to move through to get from <location A> to <location B>. This function takes into account zone of control of enemy units.
  
=== 'simplest_path' function ===
+
== 'simplest_path' function ==
  
 
  <list of locations> = simplest_path( <location A> , <location B> [, <unit location> ] )
 
  <list of locations> = simplest_path( <location A> , <location B> [, <unit location> ] )
Line 598: Line 296:
 
When only 2 parameters are specified, function returns list with all locations that unit standing on <location A> has to move through to get to <location B>. If optional 3rd parameter is specified, it returns list with all locations that unit standing on <unit location> would need to move through to get from <location A> to <location B>. This function does not take into account zone of control or enemy units.
 
When only 2 parameters are specified, function returns list with all locations that unit standing on <location A> has to move through to get to <location B>. If optional 3rd parameter is specified, it returns list with all locations that unit standing on <unit location> would need to move through to get from <location A> to <location B>. This function does not take into account zone of control or enemy units.
  
=== 'unit_at' function ===
+
== 'suitable_keep' function ==
 +
<keep location> = suitable_keep( <input location> )
 +
 
 +
Function returns location of nearest keep to the unit which is located specified <input location>.It takes a location on which a unit is standing, and returns, in order of decreasing preference: nearest (by pathfinding) empty keep reachable by that unit within 1 turn, nearest (by pathfinding) occupied keep reachable within 1 turn, result of nearest_keep() implementation, null location.
 +
 
 +
== 'unit_at' function ==
  
 
  <unit> = unit_at( <location> )  
 
  <unit> = unit_at( <location> )  
Line 606: Line 309:
 
  unit_at( loc( 4, 4) )
 
  unit_at( loc( 4, 4) )
  
=== 'nearest_keep' function ===
+
== 'nearest_keep' function ==
  
 
  <keep location> = nearest_keep( <input location> )
 
  <keep location> = nearest_keep( <input location> )
Line 612: Line 315:
 
Function returns location of nearest keep to the specified <input location>, or null if there is no keep on the map.
 
Function returns location of nearest keep to the specified <input location>, or null if there is no keep on the map.
  
=== 'nearest_loc' function ===
+
== 'nearest_loc' function ==
  
 
  <keep location> = nearest_keep( <input location>, <list of locations> )
 
  <keep location> = nearest_keep( <input location>, <list of locations> )
  
 
Function returns location that is the nearest to <input location>.
 
Function returns location that is the nearest to <input location>.
 +
For example,  " nearest_loc(loc(17,7),map(my_units,self.loc)) " - returns the location of a player-owned unit which is nearest to location (17,7)
  
=== 'unit_moves' function ===
+
== 'unit_moves' function ==
  
 
  <locations list> = unit_moves( <unit location> )
 
  <locations list> = unit_moves( <unit location> )
Line 624: Line 328:
 
Function returns list of all possible locations which unit standing at <unit location> can reach. If unit can't move, or there is no unit standing at given location, empty list is returned.
 
Function returns list of all possible locations which unit standing at <unit location> can reach. If unit can't move, or there is no unit standing at given location, empty list is returned.
  
=== 'units_can_reach' function ===
+
== 'units_can_reach' function ==
  
 
  <units list> = units_can_reach( <possible moves list>, <location> )
 
  <units list> = units_can_reach( <possible moves list>, <location> )
Line 630: Line 334:
 
Function takes as an input list of possible moves ( ai.my_moves for units that belong to AI, or ai.enemy_moves for units that belong to the opponent ) and checks which units from that list can reach <location>.
 
Function takes as an input list of possible moves ( ai.my_moves for units that belong to AI, or ai.enemy_moves for units that belong to the opponent ) and checks which units from that list can reach <location>.
  
== Recruitment ==
+
= Recruitment =
  
=== 'recruit' function ===
+
== 'recruit' function ==
  
 
  <action> = recruit( <unit name> [, <location> ] )
 
  <action> = recruit( <unit name> [, <location> ] )
Line 643: Line 347:
  
  
[[Category:Development]]
+
[[Category:AI]]
[[Category:Create]]
 
[[Category:WML Reference]]
 

Latest revision as of 17:26, 18 March 2016

Warning
Formula AI is still functional for the time being, but it is not maintained any more.

That applies to both the code and these wiki pages.

See also: Formula_AI_Howto

Overview

Syntax used to explain functions usage in this document is:

<result> = <function name>( <comma-separated list of parameters> [, <comma-separated list of optional parameters] )

Function may return <result> as:

  • <variable> - any of the supported variable types
  • <boolean> - false ( 0 or null ) or true ( 1 )
  • <unit> - unit
  • <location> - place on a gamemap
  • <action> - object, which, if later passed to 'move= ' as the result of formula evaluation, make the AI perform a desired action.
  • <result> - any of the above

Also function may return only single argument, or be able to return a whole list or a map.

There are a wide variety of functions which can be used to accomplish many different tasks. You can also Formula Language#Functions define your own functions.

In addition to the functions listed here, there are several core functions available.

Base functions

'attack' function

<action> = attack( <attacker's position>, <destination>, <attack location> [,  <weapon> ] )

The first three parameters are locations. At the begining, unit which is standing at <attacker's position> is moved to <destination> place. Then, from that place unit is attacking unit which stands in place marked by <attack location>. Fourth optional parameter is number, and indicates which weapon attacker should use - if not specified, best possible weapon is chosed automatically.

'fallback' function

<action> = fallback( [<name>] )

This function allows to chose different AI or to human player, who will take control over side untill the end of current turn. For example:

fallback()

will transfer control to the default C++ AI. You can specify a name of different AI (for example python_ai) to transfer control to it. If you want to give control to the player, use:

fallback( 'human' )

'get_unit_type' function

<unit> = get_unit_type( <unit name> )

Function returns unit_type object of desired type, for example:

get_unit_type( 'Mage' )

will result in returning unit_type of a Mage.

'move' function

<action> = move( <source> , <destination> )

Moves unit from <source> to <destination> and sets unit movement to 0. For example unit formula like:

move(my_leader.loc, loc(my_leader.loc.x, my_leader.loc.y - 1) )

will make leader move one hex north. Leader's movement points will be reset to 0.

'move_partial' function

<action> = move_partial( <source> , <destination> )

Moves unit from <source> to <destination>. For example unit formula like:

move(my_leader.loc, loc(my_leader.loc.x, my_leader.loc.y - 1) )

will make leader move one hex north.


'run_file' function

<formula> = run_file( <string_file_location> )

Parses and runs the specified .fai file. 'def'-style functions in the file are added to the function table of the current AI. File location follows the usual WML convention, for example, run_file('ai/formula/test.fai') will lead to './data/ai/formula/test.fai' being loaded.

At the moment, this function is useful mainly for debugging from the game console.

'set_unit_var' function

<action> = set_unit_var( <key>, <value> , <location>)

This action creates a new variable which is attached to the unit at <location>


'set_var' function

<action> = set_var( <key> , <value> )

This action sets new variable, for example:

set_var( 'Number one' , 1 )

Will create variable with name 'Number one' and assign 1 to it.

Evaluation

'calculate_outcome' function

[<list of outcomes>]  = calculate_outcome(<attacker current location>, <attacker location> , <defender location> , [<weapon>] )

returns a list of possible outcomes when unit standing at <attacker current location> attacks the unit at <defender location> from <attacker location>

if no weapon is provided, it will return for the weapon considered the best by the C++ weapon choice algorithm (the one used to select default weapon in normal games)

'chance to hit' function

<number> = chance_to_hit( <unit> , <location> )

This function returns how possible ( in % ) it is to hit given <unit> in a specific <location>. For example:

chance_to_hit( my_leader , my_leader.loc )

shows how easy it is to hit your leader has in a place he is currently standing on.


'evaluate_for_position' function

<variant> = evaluate_for_position(<position>, <formula> )

Returns the result of <formula> as if <formula> was evaluated with a position of <position> instead of the current position

'max_possible_damage' function

<number> = max_possible_damage( <attacking unit> , <defending unit> )

Function returns highest possible damage that <attacking unit> can inflict to <defending unit>.

'max_possible_damage_with_retaliation' function

<number> = max_possible_damage_with_retaliation( <attacking unit> , <defending unit> )

Function returns an array:

[ <attacker_melee>, <attacker_ranged>, <defender_melee>, <defender_ranged> ] 

in which first two elements are highest possible damage that <attacking unit> can inflict to <defending unit> with melee and ranged attacks, and latter two elements are highest possible damage that <defending unit> can inflict to <attacking unit> also with melee and ranged attacks.

'movement_cost' function

<number> = movement_cost( <unit> , <location> )

This function returns movement cost of given <unit> in a specific <location>. For example:

movement_cost( my_leader , my_leader.loc )

shows what movement cost your leader has in a place he is currently standing on.

'next_hop' function

 [<location>] = next_hop( src,dst, [unit_location])

this function returns the 'next hop' location to move for the unit at unit_location standing at src if it wants to move to dst. if unit_location is omitted, it defaults to src.

'outcomes' function

 [<list of positions>] = outcomes( <attack> )

this function take an attack (i.e a member of the "attacks" global variable) and return an array of positions

each position has a "chance" readable member that gives the chance of this outcome of occuring

other members of the position are not readable


positions are used as parameters for the evaluate_for_position function


'timeofday_modifier' function

<number> = timeofday_modifier(<unit> [, <location>] )

This function returns combat modifier (percentage, such as 25 for lawful unit during the day) due to time of day, taking alignment, illuminate, time of day and fearless trait into account. note: 'leadership' and 'slowed' are not taken into account.

unit - unit for which to evaluate

location - location for which to evaluate (optional, defaults to unit's current location)

Gamemap functions

'adjacent_locs' function

<loc list> = adjacent_locs( <location> )

Returns a list containing the list of the six locations adjacent to <location>


'castle_locs' function

[<loc list>] = castle_locs(<location>)


given a hex, this function will return the list of all locations that is connected to <location> only by keeps or castle. I.e the hexes you can recruit from, assumin that you are at a keep at <location>

'close enemies' function

<units list> = close_enemies( <location> , <distance> )

This function gets a list of enemies in the given or smaller distance from the location. For example:

close_enemies(loc(10,10), 5)

gives back a list of enemies in the distance of 5 tiles or less from the tile (10, 10).

'distance_between' function

<number> = distance_between( <location A> , <location B> )

This function returns distance (in hexes) between <location A> and <location B>. For example:

distance_between( loc( 1, 1) , loc( 3, 3) )

will return 3.

'distance_to_nearest_unowned_village' function

<number> = distance_to_nearest_unowned_village( <location A> )

This function returns distance (in hexes) between <location A> and nearest unowned village.

'defense_on' function

<number> = defense_on( <unit> , <location> )

This function returns defense rate of given <unit> in a specific <location>. For example:

defense_on( my_leader , my_leader.loc )

shows how good defense your leader has in a place he is currently standing on.

'find_shroud' function

<locations list> = find_shroud()

This function will return a list of locations of shrouded hexes.


'is_avoided_location' function

<boolean> = is_avoided_location(map_location)

This function will return the answer to question 'is location avoided by current ai?'

'is_unowned_village' function

<boolean> = is_unowned_village( <map or ai.map> , <location> )   #1
<boolean> = is_unowned_village( <map or ai.map> , <coordinate x> , <coordinate y> )   #2

The first argument is always a 'map' - member of the ai which provides information about the gamemap.

This will return true if we don't own the village (i.e it is unowned, owned by an ennemy, or owned by al allie)


'is_village' function

<boolean> = is_village( <map or ai.map> , <location> )   #1
<boolean> = is_village( <map or ai.map> , <coordinate x> , <coordinate y> )   #2

The first argument is always a 'map' - member of the ai which provides information about the gamemap.

In #1 usage, we put in as a second argument location. In #2, second and third arguments are numbers: coordniates of the certain hex on a map. Function checks if that place is a village, and returns either 1 (yes, it is village) or 0 (no, it isn't village). Example of usage:

is_village( map , loc( 2, 3) )
is_village( map , 2, 3)

Both check, if hex with coordinates 2,3 is a village.

Remember, when using is_village in custom function, you either have to access map by writing 'ai.map', or specify ai as a 'default input'.

'loc' function

<location> = loc( <X number>, <Y number> )

Function will return a location (pair of numbers) from two given input arguments.

'shortest_path' function

<list of locations> = shortest_path( <location A> , <location B> [, <unit location> ] )

When only 2 parameters are specified, function returns list with all locations that unit standing on <location A> has to move through to get to <location B>. If optional 3rd parameter is specified, it returns list with all locations that unit standing on <unit location> would need to move through to get from <location A> to <location B>. This function takes into account zone of control of enemy units.

'simplest_path' function

<list of locations> = simplest_path( <location A> , <location B> [, <unit location> ] )

When only 2 parameters are specified, function returns list with all locations that unit standing on <location A> has to move through to get to <location B>. If optional 3rd parameter is specified, it returns list with all locations that unit standing on <unit location> would need to move through to get from <location A> to <location B>. This function does not take into account zone of control or enemy units.

'suitable_keep' function

<keep location> = suitable_keep( <input location> )

Function returns location of nearest keep to the unit which is located specified <input location>.It takes a location on which a unit is standing, and returns, in order of decreasing preference: nearest (by pathfinding) empty keep reachable by that unit within 1 turn, nearest (by pathfinding) occupied keep reachable within 1 turn, result of nearest_keep() implementation, null location.

'unit_at' function

<unit> = unit_at( <location> ) 

This function takes only one argument - location, and returns unit if there is one standing in that location, or null otherwise. Example of usage:

unit_at( loc( 4, 4) )

'nearest_keep' function

<keep location> = nearest_keep( <input location> )

Function returns location of nearest keep to the specified <input location>, or null if there is no keep on the map.

'nearest_loc' function

<keep location> = nearest_keep( <input location>, <list of locations> )

Function returns location that is the nearest to <input location>. For example, " nearest_loc(loc(17,7),map(my_units,self.loc)) " - returns the location of a player-owned unit which is nearest to location (17,7)

'unit_moves' function

<locations list> = unit_moves( <unit location> )

Function returns list of all possible locations which unit standing at <unit location> can reach. If unit can't move, or there is no unit standing at given location, empty list is returned.

'units_can_reach' function

<units list> = units_can_reach( <possible moves list>, <location> )

Function takes as an input list of possible moves ( ai.my_moves for units that belong to AI, or ai.enemy_moves for units that belong to the opponent ) and checks which units from that list can reach <location>.

Recruitment

'recruit' function

<action> = recruit( <unit name> [, <location> ] )

This function results in recruting a unit specifed by <unit name> at first free castle hex, or at given <location>. Function:

recruit('Footpad', loc(3,3) ) 

will result in recruting Footpad at castle hex with coordinates 3,3.

This page was last edited on 18 March 2016, at 17:26.