Difference between revisions of "Wesnoth Formula Language"

From The Battle for Wesnoth Wiki
(Copy core function documentation over from FormulaAI page)
(Core Functions: Clean up, proofread, make style match Lua API docs)
Line 76: Line 76:
 
  def is_badly_wounded(u) u.hitpoints < u.max_hitpoints / 2
 
  def is_badly_wounded(u) u.hitpoints < u.max_hitpoints / 2
  
=core functions=
+
= Core Functions =
  
=== 'abs' function ===
+
=== abs ===
  
<number> = abs( <input number> )
+
* abs(''number'')
  
Function returns absolute value of an <input number>, for example
+
Function returns absolute value of an input number; for example
  
 
  abs( -5 )
 
  abs( -5 )
Line 88: Line 88:
 
will return 5.
 
will return 5.
  
=== 'choose' function ===
+
=== choose ===
  
<result> = choose( <input list> , [ <string> ,] <formula> )
+
* choose(''input'', [''self_name'',] ''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:
+
This function evaluates ''formula'' for each item in the ''input'' (which can be a list or a map). It will return the first item to which ''formula'' gave the highest value. For example:
  
 
  choose(my_units, level)
 
  choose(my_units, level)
Line 98: Line 98:
 
gives back the unit with the highest 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'.
+
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 <tt>self</tt> when necessary. The optional <self_name> parameter can be used to give a different name to the <tt>self</tt> variable.
  
When evaluating the map data type, we can reference to each key by 'key' and each value by 'value'. For example:
+
When evaluating a map, we can reference each key by <tt>key</tt> and each value by <tt>value</tt>. In this case, the <tt>self</tt> variable is this key-value pair. For example:
  
  choose( [ 'elf' -> 10, 'dwarf' -> 20 ], value )
+
  choose(['elf' -> 10, 'dwarf' -> 20 ], value)
  
Will return a key-value pair  
+
will return a key-value pair  
  
  { key->'dwarf', value->20 }
+
  {key->'dwarf', value->20}
  
=== 'contains_string' function ===
+
The curly braces are used in output to indicate that this is an object, not a map. It is not valid WFL syntax.
  
<boolean> = contains_string( <string>, <key> )
+
=== contains_string ===
  
Returns 1 if <key> can be found withing <string>, 0 otherwise
+
* contains_string(''string'', ''search_string'')
 +
 
 +
Returns 1 if ''search_string'' can be found within ''string'' (as a substring), 0 otherwise. For example:
  
 
  contains_string( 'Testing', 'ing' )
 
  contains_string( 'Testing', 'ing' )
  
returns
+
returns <tt>1</tt>.
 
 
1
 
  
=== 'debug' function ===
+
=== debug ===
  
<formula> = debug ( [<formula>] )
+
* debug([''formula''])
  
Starts a GUI formula AI debugger when evaluating a formula. It takes a formula, evaluates it and returns the result unchanged.
+
Starts a GUI formula AI debugger when evaluating a formula. It takes a formula, evaluates it and returns the result unchanged. '''Note:''' this does not appear to work in 1.13.3.
  
=== 'debug_print' function ===
+
=== debug_print ===
  
 
'''you need to enable formula log (--log-info='ai/formula_ai') to see the result of this call'''
 
'''you need to enable formula log (--log-info='ai/formula_ai') to see the result of this call'''
  
<formula> = debug_print( [ <optional string> ,] <formula)
+
* debug_print([''explanation'' ,] ''formula'' )
  
This function can be used for debging the formulas. It takes formula, writes output to the console and return it unchanged. For example:
+
This function can be used for debugging a formula. It takes a ''formula'', writes its result to the console, and returns it unchanged. For example:
  
  debug_print( [ 1, 2, 3 ] )
+
  debug_print([1, 2, 3])
  
 
will result in printing to the console
 
will result in printing to the console
  
  [ 1, 2, 3 ]
+
  [1, 2, 3]
  
Return value is the same.
+
and returning 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):
+
We can specify an optional parameter ''explanation'' that helps to distinguish between multiple ''debug_print'' calls in the same formula:
  
  debug_print( 'My array: ', [ 1, 2, 3 ] )
+
  debug_print('My array: ', [1, 2, 3])
  
 
will write in the console:
 
will write in the console:
  
  My array: [ 1, 2, 3 ]
+
  My array: [1, 2, 3]
 
 
And return
 
  
[ 1, 2, 3 ]
+
and return
  
=== 'debug_float' function ===
+
[1, 2, 3]
  
<formula> = debug_float( <location>, [ <optional string> ,] <formula>  )
+
=== debug_float ===
  
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(''location'', [''explanation'',] ''formula'' )
  
debug_float(me.loc, me.id )
+
This function can be used for debugging a formula. It takes a formula, floats a label containing the result on the hex specified (in the same way damage is displayed) and returns it unchanged. Note that the ''location'' here is an object type; it can be created with the <tt>[[#loc|loc()]]</tt> function. For example:
  
will make a label containing the id of the unit "me" float over the unit
+
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.
 
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):
+
We can specify an optional parameter ''explanation'' that helps to distinguish between multiple ''debug_float'' calls in the same formula:
  
 
  debug_float( me.loc, 'id: ', me.id )
 
  debug_float( me.loc, 'id: ', me.id )
  
will make the following label
+
will float the following label
  
  id: <unit id
+
  id: unit_id
  
And return the unit id
+
and return the unit id.
  
=== 'dir' function ===
+
=== dir ===
  
<list of names> = dir ( <input object> )
+
* dir(''object'')
  
This function return list with all names of <input object's> members. For example:
+
This function return a list of all attribtues in ''object''. For example:
  
  dir( my_leader )
+
  dir(my_leader)
  
will result in output:
+
will result in the following output:
  
 
  [ 'x', 'y', 'loc', 'id', 'leader', 'hitpoints', 'max_hitpoints', 'experience', 'max_experience', 'level',
 
  [ 'x', 'y', 'loc', 'id', 'leader', 'hitpoints', 'max_hitpoints', 'experience', 'max_experience', 'level',
 
  'total_movement', 'movement_left', 'side', 'is_enemy', 'is_mine']
 
  '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:
+
This command is useful in the formula command line, to get information about the attributes of different types of data.
 
 
dir( self )
 
  
=== 'filter' function ===
+
=== filter ===
  
<result> = filter( <input>, [ <string> ,] <formula> )
+
* filter(''input'', [''self_name'',] ''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:
+
This function will run the ''formula'' on each item in the ''input'' (which can be a list or a map). It will evaluate to a list or map which only contains items that the ''formula'' was true for. The optional <self_name> parameter can be used to give a different name to the <tt>self</tt> variable.
  
 
  filter(my_units, hitpoints < max_hitpoints)
 
  filter(my_units, hitpoints < max_hitpoints)
Line 204: Line 201:
 
will return all of your units which have less than maximum hitpoints. For instance this could be used if looking for candidates for healing.
 
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 ===
+
=== find ===
 
   
 
   
<result> = find( <input>, [ <string>,] <formula> )
+
* find(''input'', [''self_name'',] ''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:
+
This function will run <formula> on each item in the <input> (which can be a list or a map) and will return the first item for which <formula> was true. The optional <self_name> parameter can be used to give a different name to the <tt>self</tt> variable. For example:
  
 
  filter(units, id = 'Elvish Archer' )
 
  filter(units, id = 'Elvish Archer' )
  
will return first unit with id equal to 'Elvish Archer'.
+
will return the first unit with id equal to 'Elvish Archer'.
 +
 
 +
=== head ===
  
=== 'head' function ===
+
* head(''list'')
  
<variable> = head( <list of variables> )
+
Returns the first item from the ''list''; for example
  
Head returns first item from the <list of variables>, for example
+
head([5, 7, 9])
  
head( [ 5, 7, 9] )            #returns 5
+
returns <tt>5</tt>.
 
  head( [ 'Orc', 'Human' ] )    #returns 'Orc'
 
  head( [ 'Orc', 'Human' ] )    #returns 'Orc'
  
=== 'if' function ===
+
=== if ===
  
<result> = if( <condition> , <if true> [, <condition 2>, <if true 2>, ... ] [, <otherwise> ] )
+
* if(''condition'', ''if true'' [, ''condition 2'', ''if true 2'', ...] [, ''otherwise''])
  
 +
This function first evaluates the ''condition'', which is a formula. If it evaluates to true, then the function evaluates and returns the ''if true'' parameter; otherwise, it tries the next condition (if any) with the same result. If none of the conditions evaluate to true, then it returns the ''otherwise'' parameter if present, or <tt>null()</tt> 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:
 
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'))"
+
  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:
+
=== index_of ===
  
if( var = 'first', 1, var = 'second', 2, var = 'third', 3 )
+
* index_of(''value'', ''list'')
if( var = 'first', 1, var = 'second', 2, var = 'third', 3, 100 )
 
  
In case that var is equal to 'second' above will evaluate to
+
This function will return the first index where ''value'' can be found in the input ''list''. It will return -1 if the value is not found in the ''list'.
  
2
+
=== keys ===
2
 
  
And in case of var equal to 'fifth':
+
* keys(''map'')
  
null
+
Extracts the key values from an input ''map'' and returns them as a list. For example:
100
 
  
=== 'index_of' function ===
+
keys(['Elvish Fighter' -> 50, 'Elvish Archer' -> 60])
  
<result> = index_of( <value>,<list> )
+
returns
  
This function will return the first index where <value> can be found in <list>
+
['Elvish Fighter', 'Elvish Archer']
  
It will return -1 if the value is not found
+
=== map ===
  
=== 'keys' function ===
+
* map(''input'', [''self_name'',] ''formula'')
  
<result list> = keys( <input map> )
+
This function will run the ''formula'' on each item in the ''input'' (which can be a list or a map), and evaluate to a new list or map or a map, which contains the same number of items as in ''input'', with the formulas run on each item. The optional <self_name> parameter can be used to give a different name to the <tt>self</tt> variable. When run on a map, the resulting map has the same keys as the input map, but with the values updated to the results of the ''formula''. For example:
  
Extract key values from a <input map> and return them as a <result list>
+
  map([10,20], self*self)
 
 
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  
 
and  
  
  map( [10,20], 'value', value*value)
+
  map([10,20], 'value', value*value)
  
both will result in [100, 400]. Formula:
+
will both result in [100, 400]. The formula:
  
 
  map(my_units, hitpoints)  
 
  map(my_units, hitpoints)  
Line 286: Line 268:
 
will give a list back with the number of hitpoints each unit has. This is more useful in conjunction with other functions.
 
will give a list back with the number of hitpoints each unit has. This is more useful in conjunction with other functions.
  
  map( [ 'elf' -> 10, 'dwarf' -> 20 ], value*2 )
+
  map(['elf' -> 10, 'dwarf' -> 20 ], value*2)
  
Above will produce [ 'elf' -> 20, 'dwarf' -> 40 ]. Note that in case of a map data type, 'map' function can modify only the value.
+
The above will produce ['elf' -> 20, 'dwarf' -> 40]. Note that in case of a map data type, the <tt>map</tt> function can only modify the value.
  
  map( tomap([3,5,8,8]), value+key*100 )  
+
  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.
+
The above will produce <tt>[3 -> 301, 5 -> 502, 8 -> 801]</tt>. This can be used to take a list and make a map containing pairs <tt>[element_from_that_list -> f(element_from_that_list,number_of_repetitions_of_that_element_in_that_list)]</tt> where <tt>f</tt> is an arbitrary function.
  
=== 'max' function ===
+
=== max ===
  
<number> = max( <list of numbers> )
+
* max(''list of numbers'')
  
Function will return maximal number from a list,
+
Returns the largest number from the list. For example:
  
  max( [ 2, 8, -10, 3] )
+
  max([2, 8, -10, 3])
  
will return 8.
+
will return <tt>8</tt>.
  
=== 'min' function ===
+
=== min ===
  
<number> = min( <list of numbers> )
+
* min('list of numbers')
  
Function will return minimal number from a list,
+
Returns the smallest number from the list. For example:
  
 
  min( [ 3, 7, -2, 6] )
 
  min( [ 3, 7, -2, 6] )
  
will return -2.
+
will return <tt>-2</tt>.
  
 +
=== reduce ===
  
=== 'reduce' function ===
+
* reduce(''list'', ''formula'')
  
<result> = reduce( <list of variables>, <formula> )
+
This function will run the ''formula'' on the first and second members of the ''list'', then on the result and the third member and so on until the list is depleted. The final result is then returned. The two variables used in the ''formula'' are always 'a' and 'b'.
  
This function will run <formula> on the first and second members of the <list>, then on the result and the third member and so on until the list is depleted. The final result is returned.
+
For example, <tt>reduce([1,2,3,4], a+b)</tt> returns <tt>10</tt> and reduce([9,4,8,2], 10*a+b)</tt> returns <tt>9482</tt>.
The two variables used in the <formula> are 'a' and 'b'.
 
  
For example:
+
=== size ===
reduce( [1,2,3,4], a+b )
 
Returns 10
 
reduce( [9,4,8,2], 10*a+b )
 
Returns 9482
 
  
=== 'size' function ===
+
* size(''list'')
  
<number> = size( <list of variables> )
+
This function returns the number of elements in the ''list''.
  
This function returns how many variables are stored in a list:
+
For example, <tt>size([5, 7, 9])</tt> returns <tt>3</tt> and <tt>size(['Archer', 'Fighter']) returns <tt>2</tt>.
  
size( [ 5, 7, 9] )                #return 3
+
=== sort ===
size( [ 'Archer', 'Fighter' ] )  #return 2
 
  
=== 'sort' function ===
+
* sort(''list'', ''formula'')
  
<result list> = sort( <input list> , <formula> )
+
This function evaluates to a result list sorted according to the comparison ''formula'' for each item <tt>a</tt> and its successor <tt>b</tt>. For instance, sorting units according to hitpoints would be done by:
  
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)
  
sort( my_units, a.hitpoints > b.hitpoints )
+
To sort them in the reverse order, simply use <tt>&lt;</tt> instead of <tt>&gt;</tt>.
  
=== 'sum' function ===
+
=== sum ===
  
<number> = sum( <list of numbers> )
+
* sum(''list of numbers'')
  
This function evaluates to the sum of the items in the <list of numbers>. For example
+
This function evaluates to the sum of the numbers in the ''list of numbers''. For example:
  
  sum( [ 2, 5, 8] )
+
  sum([ 2, 5, 8])
  
returns 15, and:
+
returns <tt>15</tt>, and:
  
  sum( map( my_units, max_hitpoints - hitpoints ) )
+
  sum(map(my_units, max_hitpoints - hitpoints))
  
 
finds the total damage your units have taken.
 
finds the total damage your units have taken.
  
=== 'switch' function ===
+
=== switch ===
  
<result> = switch( <variable>, <value 1>, <outcome 1>, ... , <value N>, <outcome N> [, <default outcome> ] >
+
* switch(''formula'', ''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.
+
The switch function first evaluates the input ''formula'', and then checks if it is equal to any of the specified ''values''. If matching value is found, the ''outcome'' assigned to it is returned; if not, then function returns either ''default outcome'' (if specified) or null.
  
=== 'tolist' function ===
+
=== tolist ===
  
<list> = tolist( <input map> )
+
* tolist(''map'')
  
This function takes map and return a list of key-value pairs objects. For example:
+
This function takes a map and return a list of key-value pair objects. For example:
  
  tolist( [ 'Elf' -> 10, 'Dwarf' -> 20] )
+
  tolist(['Elf' -> 10, 'Dwarf' -> 20])
  
 
will return:
 
will return:
Line 377: Line 354:
 
  [{key->'Elf',value->10}, {key->'Dwarf',value->20}]
 
  [{key->'Elf',value->10}, {key->'Dwarf',value->20}]
  
=== 'tomap' function ===
+
=== tomap ===
  
<map> = tomap( <input list A> [, <input list B> ] )
+
* tomap(''list A'' [, ''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:
+
This function takes one or two ''lists'' as input and returns a map. If only one list is specified, then the function will evaluate this list, count how many similar elements are found within the list, and return a map with keys being these elements, and values being a number representing how many of them the list contains. For example:
  
  tomap( ['elf', 'dwarf', 'elf', 'elf', 'human', 'human' ] )
+
  tomap(['elf', 'dwarf', 'elf', 'elf', 'human', 'human'])
  
 
will return:
 
will return:
  
  [ 'elf' -> 3, 'dwarf' -> 1, 'human' -> 2 ]
+
  ['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.
+
If two lists are specified, then the elements of the first one will be used as a keys, and the elements of the second one as a values, when creating an output map. Note that these input lists must be of the same length. For example:
  
  tomap( [ 'elf', 'dwarf' ], [10, 20] )
+
  tomap(['elf', 'dwarf' ], [10, 20])
  
 
will result in:
 
will result in:
  
  [ 'elf' -> 10, 'dwarf' -> 20 ]
+
  ['elf' -> 10, 'dwarf' -> 20]
  
=== 'values' function ===
+
=== values ===
  
<result list> = values( <input map> )
+
* values(''map'')
  
Extract values assigned to keys from a <input map> and return them as a <result list>
+
Extracts the values assigned to keys from an input ''map'' and returns them as a list. For example:
  
  values( [ 'Elvish Fighter' -> 50, 'Elvish Archer' -> 60 ] )
+
  values(['Elvish Fighter' -> 50, 'Elvish Archer' -> 60])
  
Returns
+
returns
  
  [ 50, 60 ]
+
  [50, 60]
  
 
=== 'wave' function ===
 
=== 'wave' function ===
  
<value> = wave( <value> )
+
* wave(''number'')
  
given a value V, returns
+
Given a numeric value V, this returns:
  
 
   sin(2*pi/(V%1000/1000) )
 
   sin(2*pi/(V%1000/1000) )

Revision as of 00:04, 5 March 2016

The Wesnoth Formula Language is a functional language used to evaluate expressions within the game engine. The most common use of the WFL is in the $(formula) substitution syntax, but it can also be used in unit filters, GUI2 dialogs, and even AI.

Data Types and Operators

Numbers

The most common use of WFL is for simple calculations involving numbers. For this, the standard arithmetic operators (+ - * / %) work as you would expect, performing addition, subtraction, multiplication, division, and remainder. The only caveat to watch out for is that / rounds down when used on integers. For example, 5 / 2 will evaluate to 2. To avoid this, make sure at least one of the numbers includes a decimal point - 5.0 / 2 will evaluate to 2.5. Note that WFL supports only three decimal places of precision; beyond that it will still be rounded down. (So 1.0 / 16 will evaluate to 0.63 instead of 0.625.) The ^ operator performs exponentiation (raising to a power) - for example, 2 ^ 3 evaluates to 8

You can also use the standard comparison operators (= != < <= > >=) on numbers. This is often useful in unit filters - for example, a formula of hitpoints < max_hitpoints / 2 will match only if the unit is at less than half health. Comparison operators return 1 for true and 0 for false; there is no boolean type.

One final numeric operator exists - the dice roll. The syntax 3d12 will roll three 12-sided dice and return the sum of the results. Note however that this is not multiplayer-safe, so using it can and will produce OOS errors.

Strings

WFL also supports strings, which must be enclosed in single quotes ('like this'). The comparison operators also work on strings, performing lexicographical comparison (ie, alphabetical order). The comparison is case sensitive.

Lists

A list is a sequence of values represented as square brackets, [], surrounding a comma-separated list. For instance:

[ 1, 5, 'abc' ]

The comparison operators work on lists, performing lexicographical comparison. A specific list index can be obtained with the indexing operator, like this: my_list[index]. The first element of a list is numbered 0.

Maps

Maps: A map is a sequence of key-value pairs. For example:

[12 -> 'Hello', [1,2] -> 9, 'abc' -> 1.5]

The comparison operators work on maps. A specific element of a map can be obtained with the indexing operation. In the above example, the following conditions are all true (assuming the map is in a variable called self):

  • self[12] = 'Hello'
  • self[[1,2]] = 9
  • self['abc'] = 1.5

Other Types

There are two other basic types in WFL. One is the null type, which is returned when you attempt something invalid (such as dividing a string), and is guaranteed to be returned by the null() function, allowing you to check whether a value is null. The other is an object or container type, which possesses attributes that can be accessed using the dot (.) operator.

Variables

Formulas may have a variety of variables, depending on the context in which they are evaluated. A string substitution formula like $(3 + 5) has no variables, but a unit filter formula has variables such as hitpoints and max_hitpoints which contain various properties of the unit being tested (the same unit which is also referred to in variable substitution as $this_unit). The special variable self typically refers to the "global context" - in the case of a unit filter formula, the unit itself. This means for example that self.hitpoints and hitpoints are equivalent when used in a unit filter formula. In a string substitution formula, self is null.

A formula may declare additional variables using a where clause. This assigns a meaning to any unknown variables in the preceding formula. The general syntax is:

<formula> where <variable> = <value> [, <variable> = value ...]

This functions similarly to an operator, so if desired, you could have multiple where clauses in a single formula. Note that all variables in WFL are read-only - they are variables because they may have different values depending on the context in which the formula is evaluated, but for a given context, they are constant.

Comments

Sometimes with particularly complicated formulas, it may be useful to document what's going on inline. Of course, you can use WML comments to do this, but in some cases it may be useful to put some comments in the middle of the formula. This is easily done - simply enclose them in # characters, like this:

#This is a Wesnoth Formula Language comment.#

Note the final # - unlike WML, WFL requires this to indicate where the comment ends.

Functions

There are several predefined functions in the Wesnoth Formula Language, and if these are not enough, it is possible to define your own functions. The special variable functions always evaluates to a list of all known function names. This is mainly useful only as a debugging tool, though.

To define a function, you use the def keyword. For example:

def sgn(x) if(x < 0, -1, if(x > 0, 1, 0))

This defines a function called sgn which returns the sign of the input number.

You can select one of the function's arguments to be the "default" argument. If an object is passed to the default argument, then any attributes of that object are directly accessible in the global scope. For example:

def is_badly_wounded(u*) hitpoints < max_hitpoints / 2

This function takes a unit and returns 1 if it is at less than half hitpoints. The * indicates the default argument, without which it would instead have to be defined like this:

def is_badly_wounded(u) u.hitpoints < u.max_hitpoints / 2

Core Functions

abs

  • abs(number)

Function returns absolute value of an input number; for example

abs( -5 )

will return 5.

choose

  • choose(input, [self_name,] formula)

This function evaluates formula for each item in the input (which can be a list or a map). It will return the first item to which formula gave the highest value. For example:

choose(my_units, level)

gives back the unit with the highest level.

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. The optional <self_name> parameter can be used to give a different name to the self variable.

When evaluating a map, we can reference each key by key and each value by value. In this case, the self variable is this key-value pair. For example:

choose(['elf' -> 10, 'dwarf' -> 20 ], value)

will return a key-value pair

{key->'dwarf', value->20}

The curly braces are used in output to indicate that this is an object, not a map. It is not valid WFL syntax.

contains_string

  • contains_string(string, search_string)

Returns 1 if search_string can be found within string (as a substring), 0 otherwise. For example:

contains_string( 'Testing', 'ing' )

returns 1.

debug

  • debug([formula])

Starts a GUI formula AI debugger when evaluating a formula. It takes a formula, evaluates it and returns the result unchanged. Note: this does not appear to work in 1.13.3.

debug_print

you need to enable formula log (--log-info='ai/formula_ai') to see the result of this call

  • debug_print([explanation ,] formula )

This function can be used for debugging a formula. It takes a formula, writes its result to the console, and returns it unchanged. For example:

debug_print([1, 2, 3])

will result in printing to the console

[1, 2, 3]

and returning the same.

We can specify an optional parameter explanation that helps to distinguish between multiple debug_print calls in the same formula:

debug_print('My array: ', [1, 2, 3])

will write in the console:

My array: [1, 2, 3]

and return

[1, 2, 3]

debug_float

  • debug_float(location, [explanation,] formula )

This function can be used for debugging a formula. It takes a formula, floats a label containing the result on the hex specified (in the same way damage is displayed) and returns it unchanged. Note that the location here is an object type; it can be created with the loc() function. 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 an optional parameter explanation that helps to distinguish between multiple debug_float calls in the same formula:

debug_float( me.loc, 'id: ', me.id )

will float the following label

id: unit_id

and return the unit id.

dir

  • dir(object)

This function return a list of all attribtues in object. For example:

dir(my_leader)

will result in the following 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 the formula command line, to get information about the attributes of different types of data.

filter

  • filter(input, [self_name,] formula)

This function will run the formula on each item in the input (which can be a list or a map). It will evaluate to a list or map which only contains items that the formula was true for. The optional <self_name> parameter can be used to give a different name to the self variable.

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

  • find(input, [self_name,] formula)

This function will run <formula> on each item in the <input> (which can be a list or a map) and will return the first item for which <formula> was true. The optional <self_name> parameter can be used to give a different name to the self variable. For example:

filter(units, id = 'Elvish Archer' )

will return the first unit with id equal to 'Elvish Archer'.

head

  • head(list)

Returns the first item from the list; for example

head([5, 7, 9])

returns 5.

head( [ 'Orc', 'Human' ] )    #returns 'Orc'

if

  • if(condition, if true [, condition 2, if true 2, ...] [, otherwise])

This function first evaluates the condition, which is a formula. If it evaluates to true, then the function evaluates and returns the if true parameter; otherwise, it tries the next condition (if any) with the same result. If none of the conditions evaluate to true, then it returns the otherwise parameter if present, or null() otherwise.

For instance, an AI that recruits Wolf Riders on the first turn, and Orcish Grunts thereafter might look like this:

if(turn = 1, recruit('Wolf Rider'), recruit('Orcish Grunt'))

index_of

  • index_of(value, list)

This function will return the first index where value can be found in the input list. It will return -1 if the value is not found in the list'.

keys

  • keys(map)

Extracts the key values from an input map and returns them as a list. For example:

keys(['Elvish Fighter' -> 50, 'Elvish Archer' -> 60])

returns

['Elvish Fighter', 'Elvish Archer']

map

  • map(input, [self_name,] formula)

This function will run the formula on each item in the input (which can be a list or a map), and evaluate to a new list or map or a map, which contains the same number of items as in input, with the formulas run on each item. The optional <self_name> parameter can be used to give a different name to the self variable. When run on a map, the resulting map has the same keys as the input map, but with the values updated to the results of the formula. For example:

map([10,20], self*self)

and

map([10,20], 'value', value*value)

will both result in [100, 400]. The formula:

map(my_units, hitpoints) 

will give a list back with the number of hitpoints each unit has. This is more useful in conjunction with other functions.

map(['elf' -> 10, 'dwarf' -> 20 ], value*2)

The above will produce ['elf' -> 20, 'dwarf' -> 40]. Note that in case of a map data type, the map function can only modify the value.

map(tomap([3,5,8,8]), value+key*100)

The 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

  • max(list of numbers)

Returns the largest number from the list. For example:

max([2, 8, -10, 3])

will return 8.

min

  • min('list of numbers')

Returns the smallest number from the list. For example:

min( [ 3, 7, -2, 6] )

will return -2.

reduce

  • reduce(list, formula)

This function will run the formula on the first and second members of the list, then on the result and the third member and so on until the list is depleted. The final result is then returned. The two variables used in the formula are always 'a' and 'b'.

For example, reduce([1,2,3,4], a+b) returns 10 and reduce([9,4,8,2], 10*a+b) returns 9482.

size

  • size(list)

This function returns the number of elements in the list.

For example, size([5, 7, 9]) returns 3 and size(['Archer', 'Fighter']) returns 2.

sort

  • sort(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)

To sort them in the reverse order, simply use < instead of >.

sum

  • sum(list of numbers)

This function evaluates to the sum of the numbers 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

  • switch(formula, value 1, outcome 1 [, ... , value N, outcome N] [, default outcome])

The switch function first evaluates the input formula, and then checks if it is equal to any of the specified values. If matching value is found, the outcome assigned to it is returned; if not, then function returns either default outcome (if specified) or null.

tolist

  • tolist(map)

This function takes a map and return a list of key-value pair objects. For example:

tolist(['Elf' -> 10, 'Dwarf' -> 20])

will return:

[{key->'Elf',value->10}, {key->'Dwarf',value->20}]

tomap

  • tomap(list A [, list B])

This function takes one or two lists as input and returns a map. If only one list is specified, then the function will evaluate this list, count how many similar elements are found within the list, and return a map with keys being these elements, and values being a number representing how many of them the 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 the elements of the first one will be used as a keys, and the elements of the second one as a values, when creating an output map. Note that these input lists must be of the same length. For example:

tomap(['elf', 'dwarf' ], [10, 20])

will result in:

['elf' -> 10, 'dwarf' -> 20]

values

  • values(map)

Extracts the values assigned to keys from an input map and returns them as a list. For example:

values(['Elvish Fighter' -> 50, 'Elvish Archer' -> 60])

returns

[50, 60]

'wave' function

  • wave(number)

Given a numeric value V, this returns:

 sin(2*pi/(V%1000/1000) )