wesnoth-wiki-changes
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Wesnoth-wiki-changes] WritingYourOwnAI


From: wiki
Subject: [Wesnoth-wiki-changes] WritingYourOwnAI
Date: Thu, 4 Nov 2004 10:24 +0100

UserAgent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.3) Gecko/20041102 
Firefox/1.0RC1
IP: 168.209.98.67
URI: http://wesnoth.slack.it/?WritingYourOwnAI
 - - - - -
Index: WritingYourOwnAI
===================================================================
RCS file: /home/wesnoth/cvsroot/wikiroot/WritingYourOwnAI,v
retrieving revision 1.10
diff -u -r1.10 WritingYourOwnAI
--- WritingYourOwnAI    24 Oct 2004 21:03:21 -0000      1.10
+++ WritingYourOwnAI    4 Nov 2004 09:24:33 -0000
@@ -1,86 +1,103 @@
 Wesnoth supports a pluggable AI system that allows programmers to write their 
own AIs in C++.
 
-To write an AI, you need to derive a class from ai_interface (defined in 
ai.hpp), and implement the function play_turn
-()
+To write an AI, you need to derive a class from ''ai_interface'' (defined in 
''ai.hpp''),
+and implement the function ''play_turn()''
 which will be called every time your AI is expected to play a turn.
 
-ai_interface contains three important functions which allow you to execute the 
three basic types of move available in
-the game:
+Class ''ai_interface'' contains three important functions
+which allow you to execute the three basic types of move available in the game:
 
-* attack_enemy(), which is used to order an attack on an enemy unit,
-* move_unit(), which is used to order a unit to move from one location to 
another, and
-* recruit(), which is used to recruit a new unit.
-
-Of course, to decide where units are to move and attack, you must have 
information about the state of the game - the
-dimensions and layout of the map, the locations and type of units on the map, 
the types of units your side can recruit,
+* ''attack_enemy()'', which is used to order an attack on an enemy unit,
+* ''move_unit()'', which is used to order a unit to move from one location to 
another, and
+* ''recruit()'', which is used to recruit a new unit.
+
+Of course, to decide where units are to move and attack,
+you must have information about the state of the game -
+the dimensions and layout of the map, the locations and type of units on the 
map,
+the types of units your side can recruit,
 and information about your allies and enemies.
 
-Firstly, a type 'location' is defined, which defines any location on the map. 
It has members 'x' and 'y'. In
-pathfind.hpp there are a number of functions which will tell you useful things 
about locations -- whether two locations
-are adjacent, all the locations adjacent to a certain location, and the 
distance between locations.
-
-A type 'move_map' is defined as a std::multimap<location,location>. 
std::multimap is of course a standard C++
-container,
-and cannot be documented here. http://www.sgi.com/tech/stl/ is a good 
reference on standard C++ containers. The purpose
-of a move_map is to show all the possible moves for a side. It can either be a 
'source -> destination' map, which
-associates the locations of all the units a side has to all the possible 
places they can move to, or a 'destination ->
-source' map, which associates all the locations all the units a side has can 
get to, to all the places they are now.
-
-The function 'calculate_possible_moves' is provided as a useful utility 
function. It can give you maps for where all
-your units can move, or where all your enemy's movements can move when it's 
their turn. This is a very important
+Firstly, a type 'location' is defined, which defines any location on the map.
+It has members 'x' and 'y'. In ''pathfind.hpp'' there are a number of functions
+which will tell you useful things about locations -- whether two locations
+are adjacent, all the locations adjacent to a certain location,
+and the distance between locations.
+
+A type ''move_map'' is defined as a ''std::multimap<location,location>''.
+''std::multimap'' is of course a standard C++ container,
+and cannot be documented here. http://www.sgi.com/tech/stl/ is
+a good reference on standard C++ containers.
+The purpose of a ''move_map'' is to show all the possible moves for a side.
+It can either be a 'source -> destination' map, which
+associates the locations of all the units a side has to all the possible
+places they can move to, or a 'destination -> source' map,
+which associates all the locations all the units a side has can get to,
+to all the places they are now.
+
+The function ''calculate_possible_moves()'' is provided
+as a useful utility function. It can give you maps for where all
+your units can move, or where all your enemy's movements
+can move when it's their turn. This is a very important
 function to use to work out all the possible places your units can move to.
 
-ai_interface also defines an 'info' type. This type contains a number of 
references to various game objects which you
-will need access to in order to make moves. The two most important of these 
objects are the unit map (unit_map units)
+''ai_interface'' also defines an 'info' type.
+This type contains a number of references to various game objects which you
+will need access to in order to make moves.
+The two most important of these objects are the unit map (unit_map units)
 and the game map (gamemap map).
 
-The unit map is of type std::map<location,unit> and associates locations with 
units.
+The unit map is of type ''std::map<location,unit>''
+and associates locations with units.
 This object can be used to find the
 location of, and information about, every unit on the board.
-See unit.hpp for a definition of the 'unit' object.
+See ''unit.hpp'' for a definition of the 'unit' object.
 
 The game map allows you to inspect the dimensions and layout of the playing 
board.
 Given a location, it can tell you the
 terrain type at that location.
-See map.hpp for a definition of this object. You can combine this class with 
use of the
-functions in pathfind.hpp to find various information about where units can 
move to.
+See ''map.hpp'' for a definition of this object.
+You can combine this class with use of the
+functions in ''pathfind.hpp'' to find various
+information about where units can move to.
 
-The team class (defined in team.hpp) is also very important.
-Each side is represented by a 'team' object. The team
+The team class (defined in ''team.hpp'') is also very important.
+Each side is represented by a ''team'' object. The team
 object can tell you the gold balance of a team, which villages
 (note that internally, villages are often called 'towers')
 the team owns, what units the team can recruit,
 and which other teams are this teams friends or enemies.
 
-The utility function current_team() can be used to get a reference
+The utility function ''current_team()'' can be used to get a reference
 to the team that your AI is in control of, but you
-can also use the vector 'teams' inside the info object to get a list of all 
teams.
+can also use the vector ''teams'' inside the info object to get a list of all 
teams.
 
 If you want to make your AI customizable within the configuration file,
 you can gain access to any parameters passed to
-your AI using team::ai_parameters().
-This returns an object of type 'config' (config.hpp). These 'config' objects 
are
-representations of WML document fragments.
+your AI using ''team::ai_parameters()''.
+This returns an object of type ''config'' (see ''config.hpp'').
+These ''config'' objects are representations of WML document fragments.
 When the user defines your side, if they put an [ai] tag inside it,
-everything inside the [ai] tag will be erturned by team::ai_parameters().
+everything inside the [ai] tag will be returned by ''team::ai_parameters()''.
 
 ---
 
-Finally, when you have your AI ready to go, you can add it to the create_ai() 
function in ai.cpp. Suppose you called
-your class 'killer_ai', you could add it like so:
+Finally, when you have your AI ready to go,
+you can add it to the ''create_ai()'' function in ''ai.cpp''. Suppose you 
called
+your class ''killer_ai'', you could add it like so:
 
     if(name == "killer_ai")
         return new killer_ai(info);
 
 Then, you can define a side to use your AI in WML:
 
-ai_algorithm=killer_ai
+    ai_algorithm=killer_ai
 
 and when that side is created, it'll use your AI!
 
 ---
 
-Let us conclude with a small sample AI, called 'sample_ai'. How should this AI 
behave?
+Let us conclude with a small sample AI, called ''sample_ai''.
+How should this AI behave?
 
 * First it should detect if there are any enemies in range,
 and if there are it should attack them by moving onto the
@@ -97,7 +114,7 @@
 In the following example, I will place all functions in-line
 rather than in the cpp file. To do this properly, of
 course you should put them in the cpp file.
-The entire definition of this AI can be found in ai.cpp/ai.hpp in the
+The entire definition of this AI can be found in ''ai.cpp/ai.hpp'' in the
 source distribution.
 
 We start the definition,
@@ -106,12 +123,12 @@
     public:
          sample_ai(info& i) : ai_interface(i) {}
 
-We have defined the constructor which takes an 'info' object
+We have defined the constructor which takes an ''info'' object
 and passes it straight onto ai_interface. We don't need to
 store anything ourselves in this simple AI.
 (Although it would be fine to have data members if we wanted them.)
 
-Next we define the main function, play_turn():
+Next we define the main function, ''play_turn()'':
 
         void play_turn() {
             do_attacks();
@@ -121,7 +138,7 @@
         }
 
 Just a series of calls to functions we are about to write which do the actual 
work.
-Firstly, do_attacks(). We start by
+Firstly, ''do_attacks()''. We start by
 calculating all the moves our units can make:
 
     private:
@@ -130,16 +147,18 @@
             move_map srcdst, dstsrc;
             calculate_possible_moves(possible_moves,srcdst,dstsrc,false);
 
-Note that the 'possible_moves' thing is of little direct interest.
+Note that the ''possible_moves'' thing is of little direct interest.
 It contains details of exactly which tiles the unit
 moves along to get from one tile to another.
 This is useful for the display to know about when it draws the unit
 moving, but as an AI programmer, it's not likely you'll ever care about
 what it contains. Just pass it along to the
-move_unit() function so it can draw the unit moving along the correct path.
+''move_unit()'' function so it can draw the unit moving along the correct path.
 
-The things we're interested in are srcdst and dstsrc. Especially dstsrc. It 
will tell us all the hexes our units can
-reach. We want to check if any of these hexes are next to an enemy unit. Let's 
walk over the units and see if we can
+The things we're interested in are ''srcdst'', and especially ''dstsrc'',
+which will tell us all the hexes our units can reach.
+We want to check if any of these hexes are next to an enemy unit.
+Let's walk over the units and see if we can
 reach any of them:
 
             for(unit_map::const_iterator i = get_info().units.begin(); i != 
get_info().units.end(); ++i) {
@@ -165,20 +184,20 @@
                     int best_defense = -1;
                     std::pair<location,location> best_movement;
 
-The value of 'best_defense' will of course be between 1 and 100,
+The value of ''best_defense'' will of course be between 1 and 100,
 but we give it a value of -1 to mean 'not initialized', since we
 haven't found any possible attacks at all yet.
-Variable 'best_movement' will contain the destination/source pair that gives 
the
+Variable ''best_movement'' will contain the destination/source pair that gives 
the
 best possible defense for our attacking unit.
 
                     for(size_t n = 0; n != 6; ++n) {
                         typedef move_map::const_iterator Itor;
                         std::pair<Itor,Itor> range = 
dstsrc.equal_range(adjacent_tiles[n]);
 
-If you don't understand how 'equal_range' works,
+If you don't understand how ''equal_range'' works,
 then look up documentation on how the standard container multimap works.
-'range' now refers to all the possible movements that can end
-with our unit being at 'adjacent_tiles[n]'.
+''range'' now refers to all the possible movements that can end
+with our unit being at ''adjacent_tiles[n]''.
 Let's iterate over all those movements,
 and find if any of them give a better defensive rating than our current best 
defense.
 We'll start our iteration by creating some aliases that ensure we don't go 
crazy ;)
@@ -193,14 +212,14 @@
                             assert(un != get_info().units.end());
 
 We can assume that the unit is in that location (hence the assert),
-because calculate_possible_moves said that it's the possible source of a move.
+because ''calculate_possible_moves'' said that it's the possible source of a 
move.
 Let's find out the type of terrain we're planning to move to:
 
                             const gamemap::TERRAIN terrain = 
get_info().map.get_terrain(dst);
 
 Okay, so we have the unit, and we have the terrain,
 now we should be able to find out the unit's defensive rating on this terrain.
-The 'unit' class has a convenient 'defense_modifier' function
+The ''unit'' class has a convenient ''defense_modifier()'' function
 which will tell us the chance of hitting that unit on a certain terrain.
 
                             const int chance_to_hit = 
un->second.defense_modifier(get_info().map,terrain);
@@ -214,26 +233,29 @@
                                 best_movement = *range.first;
                             }
 
- That's it for these two loops. Iterate and close:
+That's it for these two loops. Iterate and close:
 
                             ++range.first;
                         }
                     }
 
 Now if we found a possible move, best_defense will not be -1,
-and the movement will be stored in 'best_movement'.
-So, if best_defense is -1,
-we want to move from best_movement.second to best_movement.first.
+and the movement will be stored in ''best_movement''.
+So, if ''best_defense'' is -1,
+we want to move from ''best_movement.second'' to ''best_movement.first''.
 
                     if(best_defense != -1) {
                         
move_unit(best_movement.second,best_movement.first,possible_moves);
 
-Remember that 'possible_moves' thing?
-That comes in useful here, where we have to give it to the display object so it
-can know the path to move the unit along. This is the only time we need to 
touch it.
+Remember that ''possible_moves'' thing?
+That comes in useful here,
+where we have to give it to the display object so it
+can know the path to move the unit along.
+This is the only time we need to touch it.
 
 Immediately after moving, we want to attack.
-First we need to know which weapon to use. We'll write a 'choose_weapon'
+First we need to know which weapon to use.
+We'll write a ''choose_weapon()''
 function later which will choose our weapon.
 It'll have to take the location of the attacker and the location of the
 defender, and it'll return an int referring to our weapon of choice.
@@ -246,7 +268,7 @@
 We've attacked once, but we want to attack with as many units as we can
 attack with, right? We can't use the same move_maps again,
 because they'll be invalid now that we've moved and attacked. What
-we'll do is we'll call do_attacks() all over again,
+we'll do is we'll call ''do_attacks()'' all over again,
 recursively, and return immediately. This way all our maps will be 
recalculated.
 
                          do_attacks();
@@ -258,7 +280,7 @@
 
 That's the entire function done. It'll keep attacking while it finds attacks,
 and when it finally runs out of attacks to
-execute, it'll return nicely. Let's write that choose_weapon() function now:
+execute, it'll return nicely. Let's write that ''choose_weapon()'' function 
now:
 
     int choose_weapon(const location& attacker, const location& defender) {
         const unit_map::const_iterator att = get_info().units.find(attacker);
@@ -266,7 +288,7 @@
 
         const std::vector<attack_type>& attacks = att->second.attacks();
 
-unit contains a convenient 'attacks' function
+unit contains a convenient ''attacks()'' function
 which returns a vector of all a unit's possible attacks. We'll store the
 best attack found so far, and iterate over all attacks:
 
@@ -274,7 +296,7 @@
         int best_attack = -1;
         for(int n = 0; n != attacks.size(); ++n) {
 
-There is a nice function called evaluate_battle_stats() in actions.hpp
+There is a nice function called ''evaluate_battle_stats()'' in actions.hpp
 which will give us all sorts of information about
 a potential battle. We make use of it here:
 
@@ -282,12 +304,13 @@
                   attacker, defender, n, get_info().units,
                   get_info().state, get_info().gameinfo, 0, false);
 
-A rather complicated function call,
-but most of the parameters can be pulled straight from get_info().
+A rather complicated function call, but most of
+the parameters can be pulled straight from ''get_info()''.
 The last two parameters are a little confusing:
-the first one, 'attacker_terrain_override' is used if we wanted to know what 
the
-combat would look like if the attacker was on different terrain
-to what it is on now. If this is non-0, the function will assume
+the first one, ''attacker_terrain_override'' is used
+if we wanted to know what the combat would look like
+if the attacker was on different terrain to what it is on now.
+If this is non-0, the function will assume
 the attacker is on the type of terrain given.
 This is useful if you want to test the possibility of moving
 to many different hexes without actually moving there.
@@ -312,7 +335,8 @@
          return best_attack;
     }
 
-Now we're done with that, we can move onto our get_villages() function. We 
start off by calculating possible moves,
+Now we're done with that, we can move onto our ''get_villages()'' function.
+We start off by calculating possible moves,
 
     void get_villages() {
         std::map<location,paths> possible_moves;
@@ -327,13 +351,13 @@
                current_team().owns_tower(i->first) == false) {
 
 This may look a little intimidating, but it's fairly simple:
-map.get_terrain gets the terrain at a certain location.
-We want to pass it through 'underlying_terrain',
+''map.get_terrain'' gets the terrain at a certain location.
+We want to pass it through ''underlying_terrain()'',
 because it may be a terrain alias -- for instance a human village, a
 merman village, or a snow village. Then we see if it's equal to
-gamemap::TOWER to see if its underlying type is in fact a village.
+''gamemap::TOWER'' to see if its underlying type is in fact a village.
 
-The right side of the && simply sees if our team owns the village
+The right side of the ''&&'' simply sees if our team owns the village
 at that location or not. If we don't own the village,
 we've found the movement we want to make, and we recurse and return.
 
@@ -345,7 +369,7 @@
     }
 
 
-Just a couple more functions now. Firstly, do_moves() is meant
+Just a couple more functions now. Firstly, ''do_moves()'' is meant
 to move our units toward the enemy leader. Well
 hmm...there may be multiple enemies and thus more than one leader,
 so we'll just go for the first enemy leader we can
@@ -355,8 +379,8 @@
         unit_map::const_iterator leader;
         for(leader = get_info().units.begin(); leader != 
get_info().units.end(); ++leader) {
 
-A unit is a leader if it can recruit -- so we use the 'can_recruit'
-function to test if it's a leader.
+A unit is a leader if it can recruit -- so we use
+the ''can_recruit()'' function to test if it's a leader.
 
             if(leader->second.can_recruit() && 
current_team().is_enemy(leader->second.side())) {
                 break;
@@ -419,17 +443,17 @@
             do_recruitment();
         }
     }
-};
+ };
 
-That's it! We've made our sample_ai.
-All we have to do is add it to create_ai in ai.cpp and we're done!
+That's it! We've made our ''sample_ai''.
+All we have to do is add it to ''create_ai'' in ''ai.cpp'' and we're done!
 
 ||AI - specific parameters||
 
-wesnoth --multiplayer --controller1=ai --controller2=ai --algorithm1=z_ai 
--algorithm2=sample_ai
+    wesnoth --multiplayer --controller1=ai --controller2=ai --algorithm1=z_ai 
--algorithm2=sample_ai
 
-Use the --nogui switch before --multiplayer to make the game run without 
displaying a gui. The winner will be reported
-on stdout.
+Use the ''--nogui'' switch before ''--multiplayer'' to make the game
+run without displaying a GUI.  The winner will be reported on stdout.
 
 ||See Also||
 






reply via email to

[Prev in Thread] Current Thread [Next in Thread]