Formula AI Code Library

From The Battle for Wesnoth Wiki
Revision as of 02:34, 26 November 2011 by Coffee (talk | contribs)

This page lists Formula AI code examples that can be used directly in a scenario or as templates for further development. If you have problems with any of them or would like to see additional examples let us know in this forum thread. If you have examples to add, you can also let us know there and we will post them, or feel free to edit these wiki pages yourself.

  • Check out Formula AI Howto for a practical guide for setting up and testing these examples in a scenario.
  • Additional examples can be found in the Wesnoth data directory under ai/formula/. See EditingWesnoth for locating the data directory.
  • Lua AI code examples can be found in the Lua AI Code Library.

Swamp Lurker Moves

This is the Formula AI adaptation of the WML swamp lurker code from Grnk the Mighty. Swamp lurkers have the 'swamp_submerge' ability, meaning they are invisible in swamps unless an enemy unit is adjacent to them. They are dumb, impulse-drive creatures which move as follows:

  • They can move across most terrain, but only stop on swamp.
  • All lurkers move individually, there is no strategy.
  • If there are enemies within reach (next to swamp terrain), a lurker will attack the unit with the fewest hitpoints.
  • If no unit is in reach, the lurker will move to a random reachable swamp hex.

In Grnk, this behavior is coded in WML (the WML code can be found in '1_unit_macros.cfg' in the Grnk folder or in this forum post). The adaptation to Formula AI follows below. A Lua AI version exists as well.

fai 'lurker_moves.fai'
# run_file('~add-ons/AItest/ai/lurker_moves.fai') #

def is_swamp(map, xx, yy)   
    # Tests whether terrain at xx,yy is swamp_water #
    map( 
        filter( map.terrain, (x=xx) and (y=yy)),
        self.id 
    )[0] = 'swamp_water';

def reachable_swamp(unit_loc,map)
    # get all reachable swamp locs for unit at unit_loc #
    # exclude own location #
    filter( 
        unit_moves( unit_loc ), 
        (is_swamp( map, x, y )) and (self != unit_loc)
    );

def random(array)
    # Picks a random elements of 'array' #
    array[(1d size(array) - 1)];

def reachable_enemies_next_to_swamp(unit_loc,attacks,map)
    # Returns all the attacks for enemies that the unit at 'unit_loc' #
    # can reach and that are next to a swamp hex #
    filter( 
        map( attacks.attacks, self),
        (move_from = unit_loc) and (is_swamp( map, attack_from.x, attack_from.y))
    );

def weakest_defender(attacks)
    # Get the enemy with the lowest HP #
    choose( attacks, -unit_at(defender).hitpoints
    );

# Attack if possible, otherwise random move #
if( size(possible_attacks) != 0,
    weakest_defender(possible_attacks),
if( size(swamp_in_reach) != 0,
    move(me.loc,random(swamp_in_reach)),
end)
)

where possible_attacks = reachable_enemies_next_to_swamp( me.loc, my_attacks, map)

where swamp_in_reach = reachable_swamp(me.loc, map)

faiend

This code is meant to be in a file 'lurker_moves.fai. It can be used with the Formula AI unit_moves stage by including

[ai]
    formula={lurker_moves.fai}
[/ai]

in the definition of all swamp lurker units. Or it can be set up as a candidate action that applies to all swamp lurker units of a given side:

[candidate_action]
     engine=fai
     name=lurker_moves
     type=movement
     [filter]
         me="filter(input, (input.type = 'Swamp Lurker'))"
     [/filter]
     evaluation=300000
     action={lurker_moves.fai}
[/candidate_action]

Notes:

  • This code can, of course, also be used with any non-lurker unit as long as it is able to move through swamp.
  • This example currently only works with "pure" swamp, not with quagmire tiles, but the extension to include those should be obvious.

Recruiting certain types of units on particular hexes

This Formula AI example is from a multiplayer campaign, The Great Quest, by Coffee.

As it currently stands in Wesnoth 1.9, the AI recruits units based on a [i recrutment_pattern] AI parameter. This sometimes can lead to non-optimal scout recruiting, etc. The following code attempts to remedy that by suggesting that certain units be recruited on certain hexes to improve village grabbing, etc. (but could also be used to recruit a wider variety of units within each [i recrutment_pattern] parameter).

This code specifies to recruit an appropriate scout (from default faction units) to grab a village 8 tiles away on (7, 24) by flat for side 4. *Note: undead scouts are excluded as ghost only moves 7 hexes and saurians as they move only 6, with quick scout moving 7.

[event]
     name=turn 1
     
     #recruit 8 movement scout unit to get village(7,24) fast
     {MODIFY_AI_ADD_CANDIDATE_ACTION 4 main_loop (
         [candidate_action]
         id=recruit_scout
         name=recruit_scout
         engine=fai
         type=movement
         evaluation="if(turn=1 and unit_at(loc(7,24))=null(), {AI_CA_RECRUITMENT_SCORE}+400, 0)"
         action="recruit(scout_units[(1 d size(scout_units))-1],loc(7,24))
         where scout_units=map(filter(my_recruits,usage='scout' and undead=0),id)"
         [/candidate_action]
     )}
     [/event]
     
  1. delete at turn 2, as we should have the wanted scout recruited by then
     [event]
     name=turn 2
     
     {MODIFY_AI_TRY_DELETE_CANDIDATE_ACTION 4 main_loop recruit_scout}
     [/event]