ai_village.cpp

Go to the documentation of this file.
00001 /* $Id: ai_village.cpp 22874 2008-01-11 11:49:14Z mordante $ */
00002 /*
00003    Copyright (C) 2008 by Mark de Wever <koraq@xs4all.nl>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013 */
00014 
00015 //! @file ai_village.cpp
00016 //! The village capturing part of the AI.
00017 //! ai::get_villages and ai::find_villages are based on ai::get_villages is ai.cpp
00018 
00019 #include "global.hpp"
00020 
00021 #include "ai.hpp"
00022 #include "log.hpp"
00023 
00024 #include <cassert>
00025 #include <numeric>
00026 
00027 #define DBG_AI LOG_STREAM(debug, ai) 
00028 #define LOG_AI LOG_STREAM(info, ai) 
00029 #define WRN_AI LOG_STREAM(warn, ai)
00030 
00031 // Basic strategy
00032 // 1. Store all our units that can move.
00033 //
00034 // 2. Test all reachable locations to be a proper village.
00035 //
00036 // 3. How many units can reach the village?
00037 //    - If 0 ignore village.
00038 //    - If 1 capture and remove unit from available list.
00039 //    - Else store.
00040 // 
00041 // 4. Remove units who can't reach a village.
00042 //
00043 // 5. Dispatch single solutions:
00044 //    - Dispatch units that can capture 1 village.
00045 //    - After this dispatch some units might not be able to reach
00046 //      anything anymore, remove them.
00047 //    - How many units left?
00048 //      - If 1 dispatch to village.
00049 //    - For villages that can be captured by 1 unit, dispatch that unit.
00050 //    - how many villages left?
00051 //      - If 1 dispatch a unit.
00052 // 
00053 // 6. If no units we finish.
00054 //
00055 // At this point we have X villages with Y units left where 
00056 // X > 1 && Y > 1 and every unit can reach at least 2 villages
00057 // and every village can be visited by at least 2 units.
00058 //
00059 // 7. Can every unit visit every village?
00060 //    - Yes dispatch them first unit first village
00061 //      second unit second village etc.
00062 //
00063 //  In the following example:
00064 //
00065 //  village x1,y1   x2,y2   x3,y3   x4,y4
00066 //  unit  
00067 //  1       X       X       X       X
00068 //  2       -       X       X       -
00069 //  3       X       -       -       X
00070 //
00071 //  We want to find squares of 2 units who both can reach the same village. 
00072 //
00073 //  village x1,y1   x2,y2   x3,y3   x4,y4
00074 //  unit  
00075 //                 ___________
00076 //  1       X|     |X       X|     |X
00077 //          _|     |         |     |_
00078 //                 |         |
00079 //  2       -      |X       X|      -
00080 //                 |_________|
00081 //          __                     __
00082 //  3       X|      -       -      |X
00083 //   
00084 //  8. - Find a square where at least 1 unit can visit only 2 villages.
00085 //     - Dispatch the units.
00086 //     - Could the second unit also visit 2 villages and where the 2 villages
00087 //       only visitable by two units?
00088 //       - Yes found a perfect solution for them 
00089 //         - Reduce the village count.
00090 //         - Restart ourselves recursively.
00091 //         - Finsished.
00092 //
00093 //       - No. 
00094 //         - Remove the taken villages from the list.
00095 //         - Go to step 5.
00096 //
00097 //  9. Didn't find a solution, test all permutations.
00098 
00099 namespace {
00100     //! Location of the keep the closest to our leader.
00101     gamemap::location keep_loc = gamemap::location::null_location;
00102     //! Locaton of our leader.
00103     gamemap::location leader_loc = gamemap::location::null_location;
00104     //! The best possible location for our leader if it can't
00105     //! reach a village.
00106     gamemap::location best_leader_loc = gamemap::location::null_location;
00107 
00108     //! debug log level for AI enabled?
00109     bool debug = false;
00110 
00111     typedef std::map<gamemap::location /* unit location */, 
00112         std::vector<gamemap::location /* villages we can reach */> > treachmap;
00113 
00114     typedef std::vector<std::pair<gamemap::location /* destination */, 
00115         gamemap::location /* start */ > > tmoves;
00116 }
00117 
00118 //! Dispatches all units to their best location.
00119 static void dispatch(treachmap& reachmap, tmoves& moves);
00120 
00121 //! Dispatches all units who can reach one village.
00122 //! Returns true if it modified reachmap isn't empty
00123 static bool dispatch_unit_simple(treachmap& reachmap, tmoves& moves);
00124 
00125 //! Dispatches units to villages which can only be reached by one unit.
00126 //! Returns true if modified reachmap and reachmap isn't empty
00127 static bool dispatch_village_simple(
00128     treachmap& reachmap, tmoves& moves, size_t& village_count);
00129 
00130 //! Removes a village for all units, returns true if anything is deleted.
00131 static bool remove_village(
00132     treachmap& reachmap, tmoves& moves, const gamemap::location& village);
00133 
00134 //! Removes a unit which can't reach any village anymore.
00135 static treachmap::iterator remove_unit(
00136     treachmap& reachmap, tmoves& moves, treachmap::iterator unit);
00137 
00138 //! Dispatches the units to a village after the simple dispatching failed.
00139 static void dispatch_complex(
00140     treachmap& reachmap, tmoves& moves, const size_t village_count);
00141 
00142 //! Dispatches all units to a village, every unit can reach every village.
00143 static void full_dispatch(treachmap& reachmap, tmoves& moves);
00144 
00145 //! Shows which villages every unit can reach (debug function).
00146 static void dump_reachmap(treachmap& reachmap);
00147 
00148 bool ai::get_villages(std::map<gamemap::location,paths>& possible_moves,
00149         const move_map& dstsrc, const move_map& enemy_dstsrc, 
00150         unit_map::iterator &leader)
00151 {
00152     DBG_AI << "deciding which villages we want...\n";
00153     const int ticks = SDL_GetTicks();
00154     best_leader_loc = gamemap::location::null_location;
00155     if(leader != units_.end()) {
00156         keep_loc = nearest_keep(leader->first);
00157         leader_loc = leader->first;
00158     } else {
00159         keep_loc = gamemap::location::null_location;
00160         leader_loc = gamemap::location::null_location;
00161     }
00162 
00163     debug = (!lg::debug.dont_log(lg::ai));
00164 
00165     // Find our units who can move.
00166     treachmap reachmap; 
00167     for(unit_map::const_iterator u_itor = units_.begin();
00168             u_itor != units_.end(); ++u_itor) {
00169 
00170         if(u_itor->second.side() == team_num_ 
00171                 && u_itor->second.movement_left()) {
00172 
00173             reachmap.insert(std::make_pair(u_itor->first,   std::vector<gamemap::location>()));
00174         }
00175     }
00176 
00177     // The list of moves we want to make
00178     tmoves moves;
00179 
00180     DBG_AI << reachmap.size() << " units found who can try to capture a village.\n";
00181 
00182     find_villages(reachmap, moves, dstsrc, possible_moves, enemy_dstsrc);
00183 
00184     treachmap::iterator itor = reachmap.begin();
00185     while(itor != reachmap.end()) {
00186         if(itor->second.size() == 0) {
00187             itor = remove_unit(reachmap, moves, itor);
00188         } else {
00189             ++itor;
00190         }
00191     }
00192 
00193     if(reachmap.size()) {
00194         DBG_AI << reachmap.size() << " units left after removing the ones who "
00195             "can't reach a village, send the to the dispatcher.\n";
00196 
00197         dump_reachmap(reachmap);
00198 
00199         dispatch(reachmap, moves);
00200     } else {
00201         DBG_AI << "No more units left after removing the ones who can't reach a village.\n";
00202     }
00203 
00204     LOG_AI << "Village assignment done: " << (SDL_GetTicks() - ticks)
00205         << " ms, resulted in " << moves.size() << " units being dispatched.\n";
00206 
00207     // Move all the units to get villages, however move the leader last,
00208     // so that the castle will be cleared if it wants to stop to recruit along the way.
00209     std::pair<location,location> leader_move;
00210 
00211     int moves_made = 0;
00212     for(tmoves::const_iterator i = moves.begin(); i != moves.end(); ++i) {
00213 
00214         if(leader != units_.end() && leader->first == i->second) {
00215             leader_move = *i;
00216         } else {
00217             if(units_.count(i->first) == 0) {
00218                 const location loc = move_unit(i->second,i->first,possible_moves);
00219                 ++moves_made;
00220                 leader = find_leader(units_, team_num_);
00221 
00222                 // If we didn't make it to the destination, it means we were ambushed.
00223                 if(loc != i->first) {
00224                     return true;
00225                 }
00226 
00227                 const unit_map::const_iterator new_unit = units_.find(loc);
00228 
00229                 if(new_unit != units_.end() &&
00230                         power_projection(i->first,enemy_dstsrc) >= new_unit->second.hitpoints()/4) {
00231                     add_target(target(new_unit->first,1.0,target::SUPPORT));
00232                 }
00233             }
00234         }
00235     }
00236 
00237     if(leader_move.second.valid()) {
00238         if(units_.count(leader_move.first) == 0) {
00239             gamemap::location loc = move_unit(leader_move.second,leader_move.first,possible_moves);
00240             ++moves_made;
00241             // Update leader iterator, since we moved it.
00242             leader = units_.find(loc);
00243         }
00244     }
00245 
00246     return false;
00247 }
00248 
00249 void ai::find_villages(
00250     treachmap& reachmap,
00251     tmoves& moves,
00252     const std::multimap<gamemap::location,gamemap::location>& dstsrc,
00253     const std::map<gamemap::location,paths>& possible_moves,
00254     const std::multimap<gamemap::location,gamemap::location>& enemy_dstsrc) const
00255 
00256 {
00257     std::map<location, double> vulnerability;
00258 
00259     const bool passive_leader = 
00260         utils::string_bool(current_team().ai_parameters()["passive_leader"]);
00261 
00262     size_t min_distance = 100000;
00263     
00264     // When a unit is dispatched we need to make sure we don't
00265     // dispatch this unit a second time, so store them here.
00266     std::vector<gamemap::location> dispatched_units;
00267     for(std::multimap<gamemap::location, gamemap::location>::const_iterator 
00268             j = dstsrc.begin();
00269             j != dstsrc.end(); ++j) {
00270 
00271         const gamemap::location& current_loc = j->first;
00272 
00273         if(j->second == leader_loc) {
00274             if(passive_leader) {
00275                 continue;
00276             }
00277 
00278             const size_t distance = distance_between(keep_loc, current_loc);
00279             if(distance < min_distance) {
00280                 min_distance = distance;
00281                 best_leader_loc = current_loc;
00282             }
00283         }
00284 
00285         if(std::find(dispatched_units.begin(), dispatched_units.end(),
00286                 j->second) != dispatched_units.end()) {
00287             continue;
00288         }
00289 
00290         if(map_.is_village(current_loc) == false) {
00291             continue;
00292         }
00293 
00294         bool want_village = true, owned = false;
00295         for(size_t n = 0; n != teams_.size(); ++n) {
00296             owned = teams_[n].owns_village(current_loc);
00297             if(owned && !current_team().is_enemy(n+1)) {
00298                 want_village = false;
00299             }
00300 
00301             if(owned) {
00302                 break;
00303             }
00304         }
00305 
00306         if(want_village == false) {
00307             continue;
00308         }
00309 
00310         // If it is a neutral village, and we have no leader,
00311         // then the village is of no use to us, and we don't want it.
00312         if(!owned && leader_loc == gamemap::location::null_location) {
00313             continue;
00314         }
00315 
00316         // If we have a decent amount of gold, and the leader can't access
00317         // the keep this turn if they get this village,
00318         // then don't get this village with them.
00319         if(want_village &&
00320                 current_team().gold() > 20 &&
00321                 leader_loc == current_loc &&
00322                 leader_loc != keep_loc &&
00323                 multistep_move_possible(j->second, current_loc, keep_loc, possible_moves) == false) {
00324             continue;
00325         }
00326 
00327         double threat = 0.0;
00328         const std::map<location,double>::const_iterator vuln = vulnerability.find(current_loc);
00329         if(vuln != vulnerability.end()) {
00330             threat = vuln->second;
00331         } else {
00332             threat = power_projection(current_loc,enemy_dstsrc);
00333             vulnerability.insert(std::pair<location,double>(current_loc,threat));
00334         }
00335 
00336         const unit_map::const_iterator u = units_.find(j->second);
00337         if(u == units_.end() || utils::string_bool(u->second.get_state("guardian"))) {
00338             continue;
00339         }
00340 
00341         const unit& un = u->second;
00342         if(un.hitpoints() < (threat*2*un.defense_modifier(map_.get_terrain(current_loc)))/100) {
00343             continue;
00344         }
00345 
00346         // If the next and previous destination differs from our current destination, 
00347         // we're the only one who can reach the village -> dispatch.
00348         std::multimap<gamemap::location, gamemap::location>::const_iterator next = j;
00349         ++next; // j + 1 fails
00350         const bool at_begin = (j == dstsrc.begin());
00351         std::multimap<gamemap::location, gamemap::location>::const_iterator prev = j; //FIXME seems not to work
00352         if(!at_begin) {
00353             --prev;
00354         }
00355 #if 1       
00356         if((next == dstsrc.end() || next->first != current_loc) 
00357                 && (at_begin || prev->first != current_loc)) {
00358 
00359             DBG_AI << "Dispatched unit at " << j->second << " to village " << j->first << '\n';
00360 
00361             moves.push_back(std::make_pair(j->first, j->second));
00362             reachmap.erase(j->second);
00363             dispatched_units.push_back(j->second);
00364             continue;
00365         }
00366 #endif      
00367         reachmap[j->second].push_back(current_loc);
00368     }
00369 
00370     DBG_AI << moves.size() << " units already dispatched, " 
00371         << reachmap.size() << " left to evaluate.\n";
00372 }
00373 
00374 static void dispatch(treachmap& reachmap, tmoves& moves)
00375 {
00376     DBG_AI << "Starting simple dispatch.\n";
00377 
00378     // we now have a list with units with the villages they can reach.
00379     // keep trying the following steps as long as one of them changes
00380     // the state.
00381     // 1. Dispatch units who can reach 1 village (if more units can reach that
00382     //    village only one can capture it, so use the first in the list.)
00383     // 2. Villages which can only be reached by one unit get that unit dispatched
00384     //    to them.
00385     size_t village_count = 0;
00386     bool dispatched = true;
00387     while(dispatched) {
00388         dispatched = false;
00389 
00390         if(dispatch_unit_simple(reachmap, moves)) {
00391             dispatched = true;
00392         } else {
00393             if(reachmap.empty()) {
00394                 DBG_AI << "dispatch_unit_simple() found a final solution.\n";
00395                 break;
00396             } else {
00397                 DBG_AI << "dispatch_unit_simple() couldn't dispatch more units.\n";
00398             }
00399         }           
00400 
00401         if(dispatch_village_simple(reachmap, moves, village_count)) {
00402             dispatched = true;
00403         } else {
00404             if(reachmap.empty()) {
00405                 DBG_AI << "dispatch_village_simple() found a final solution.\n";
00406                 break;
00407             } else {
00408                 DBG_AI << "dispatch_village_simple() couldn't dispatch more units.\n";
00409             }
00410         }
00411         
00412         if(reachmap.size() != 0 && dispatched) {
00413             DBG_AI << reachmap.size() << " unit(s) left restarting simple dispatching.\n";
00414 
00415             dump_reachmap(reachmap);
00416         }
00417     }
00418 
00419     if(reachmap.size() == 0) {
00420         DBG_AI << "No units left after simple dispatcher.\n";
00421         return;
00422     }
00423 
00424     DBG_AI << reachmap.size() << " units left for complex dispatch with " 
00425         << village_count << " villages left.\n";
00426 
00427     dump_reachmap(reachmap);
00428 
00429     dispatch_complex(reachmap, moves, village_count);
00430 }
00431 
00432 // Returns      need further processing
00433 // false        Nothing has been modified or no units left
00434 static bool dispatch_unit_simple(treachmap& reachmap, tmoves& moves)
00435 {
00436     bool result = false;
00437     
00438     treachmap::iterator itor = reachmap.begin();
00439     while(itor != reachmap.end()) {
00440         if(itor->second.size() == 1) {
00441             const gamemap::location village = itor->second[0];
00442             result = true;
00443 
00444             DBG_AI << "Dispatched unit at " << itor->first << " to village " << village << '\n';
00445             moves.push_back(std::make_pair(village, itor->first));
00446             reachmap.erase(itor++);
00447 
00448             if(remove_village(reachmap, moves, village)) {
00449                 itor = reachmap.begin();
00450             }
00451 
00452         } else {
00453             ++itor;
00454         }
00455     }
00456 
00457     // Test special cases.
00458     if(reachmap.empty()) {
00459         // We're done.
00460         return false;
00461     }
00462 
00463     if(reachmap.size() == 1) {
00464         // One unit left.
00465         DBG_AI << "Dispatched _last_ unit at " << reachmap.begin()->first 
00466             << " to village " << reachmap.begin()->second[0] << '\n';
00467 
00468         moves.push_back(std::make_pair(
00469             reachmap.begin()->second[0], reachmap.begin()->first));
00470 
00471         reachmap.clear();
00472         // We're done.
00473         return false;
00474     }
00475 
00476     return result;
00477 }
00478 
00479 static bool dispatch_village_simple(
00480     treachmap& reachmap, tmoves& moves, size_t& village_count)
00481 {
00482 
00483     bool result = false;
00484     bool dispatched = true;
00485     while(dispatched) {
00486         dispatched = false;
00487 
00488         // build the reverse map
00489         std::map<gamemap::location /*village location*/, 
00490             std::vector<gamemap::location /* units that can reach it*/> >reversemap;
00491 
00492         treachmap::const_iterator itor = reachmap.begin();
00493         for(;itor != reachmap.end(); ++itor) {
00494 
00495             for(std::vector<gamemap::location>::const_iterator 
00496                     v_itor = itor->second.begin();
00497                     v_itor != itor->second.end(); ++v_itor) {
00498 
00499                 reversemap[*v_itor].push_back(itor->first);
00500 
00501             }
00502         }
00503 
00504         village_count = reversemap.size();
00505 
00506         itor = reversemap.begin();
00507         while(itor != reversemap.end()) {
00508             if(itor->second.size() == 1) {
00509                 // One unit can reach this village.
00510                 const gamemap::location village = itor->first;
00511                 dispatched = true;
00512                 result = true;
00513 
00514                 DBG_AI << "Dispatched unit at " << itor->second[0] << " to village " << itor->first << '\n';
00515                 moves.push_back(std::make_pair(itor->first, itor->second[0]));
00516 
00517                 reachmap.erase(itor->second[0]);
00518                 remove_village(reachmap, moves, village);
00519                 // Get can go to some trouble to remove the unit from the other villages
00520                 // instead we abort this loop end do a full rebuild on the map.
00521                 break;
00522             } else {
00523                 ++itor;
00524             }
00525         }
00526     }
00527 
00528     return result;
00529 }
00530 
00531 static bool remove_village(
00532     treachmap& reachmap, tmoves& moves, const gamemap::location& village)
00533 {
00534     bool result = false;
00535     treachmap::iterator itor = reachmap.begin();
00536     while(itor != reachmap.end()) {
00537         itor->second.erase(std::remove(itor->second.begin(), itor->second.end(), village), itor->second.end());
00538         if(itor->second.empty()) {
00539             result = true;
00540             itor = remove_unit(reachmap, moves, itor);
00541         } else {
00542             ++itor;
00543         }
00544     }
00545     return result;
00546 }
00547 
00548 static treachmap::iterator remove_unit(
00549     treachmap& reachmap, tmoves& moves, treachmap::iterator unit)
00550 {
00551     assert(unit->second.empty());
00552 
00553     if(unit->first == leader_loc && best_leader_loc != gamemap::location::null_location) {
00554         DBG_AI << "Dispatch leader at " << leader_loc << " closer to the keep at " 
00555             << best_leader_loc << '\n';
00556 
00557         moves.push_back(std::make_pair(best_leader_loc, leader_loc));
00558     }
00559 
00560     reachmap.erase(unit++);
00561     return unit;
00562 }
00563 
00564 static void dispatch_complex(
00565     treachmap& reachmap, tmoves& moves, const size_t village_count)
00566 {
00567     // ***** ***** Init and dispatch if every unit can reach every village.
00568 
00569     const size_t unit_count = reachmap.size();
00570     // The maximum number of villages we can capture with the available units.
00571     const size_t max_result = unit_count < village_count ? unit_count : village_count;
00572 
00573     assert(unit_count >= 2 && village_count >= 2);
00574 
00575     // Every unit can reach every village.
00576     if(unit_count == 2 && village_count == 2) {
00577         DBG_AI << "Every unit can reach every village for 2 units, dispatch them.\n";
00578         full_dispatch(reachmap, moves);
00579         return;
00580     }
00581 
00582     std::vector<gamemap::location> units(unit_count);
00583     std::vector<size_t> villages_per_unit(unit_count);
00584     std::vector<gamemap::location> villages;
00585     std::vector<size_t> units_per_village(village_count);
00586 
00587     // We want to test the units, the ones who can reach the least
00588     // villages first so this is our lookup map.
00589     std::multimap<size_t /* villages_per_unit value*/, 
00590         size_t /*villages_per_unit index*/> unit_lookup;
00591 
00592     std::vector</*unit*/std::vector</*village*/bool> > 
00593         matrix(reachmap.size(), std::vector<bool>(village_count, false));
00594 
00595     treachmap::const_iterator itor = reachmap.begin();
00596     for(size_t u = 0; u < unit_count; ++u, ++itor) {
00597         units[u] = itor->first;
00598         villages_per_unit[u] = itor->second.size();
00599         unit_lookup.insert(std::make_pair(villages_per_unit[u], u));
00600 
00601         assert(itor->second.size() >= 2);
00602 
00603         for(size_t v = 0; v < itor->second.size(); ++v) {
00604 
00605             size_t v_index;
00606             // find the index of the v in the villages
00607             std::vector<gamemap::location>::const_iterator v_itor = 
00608                 std::find(villages.begin(), villages.end(), itor->second[v]);
00609             if(v_itor == villages.end()) {
00610                 v_index = villages.size(); // will be the last element after push_back.
00611                 villages.push_back(itor->second[v]);
00612             } else {
00613                 v_index = v_itor - villages.begin();
00614             }
00615 
00616             units_per_village[v_index]++;
00617             
00618             matrix[u][v_index] = true;
00619         }
00620     }
00621     for(std::vector<size_t>::const_iterator upv_it = units_per_village.begin();
00622             upv_it != units_per_village.end(); ++upv_it) {
00623 
00624         assert(*upv_it >=2);
00625     }
00626 
00627     if(debug) {
00628         // Print header
00629         std::cerr << "Reach matrix:\n\nvillage";
00630         size_t u, v;
00631         for(v = 0; v < village_count; ++v) {
00632             std::cerr << '\t' << villages[v];
00633         }
00634         std::cerr << "\ttotal\nunit\n";
00635 
00636         // Print data
00637         for(u = 0; u < unit_count; ++u) {
00638             std::cerr << units[u];
00639 
00640             for(size_t v = 0; v < village_count; ++v) {
00641                 std::cerr << '\t' << matrix[u][v];
00642             }
00643             std::cerr << "\t" << villages_per_unit[u] << '\n';
00644         }
00645 
00646         // Print footer
00647         std::cerr << "total";
00648         for(v = 0; v < village_count; ++v) {
00649             std::cerr << '\t' << units_per_village[v];
00650         }
00651         std::cerr << '\n';
00652     }
00653 
00654     // Test the special case, everybody can reach all villages
00655     const bool reach_all = ((village_count == unit_count) 
00656         && (std::accumulate(villages_per_unit.begin(), villages_per_unit.end(), size_t()) 
00657         == (village_count * unit_count)));
00658 
00659     if(reach_all) {
00660         DBG_AI << "Every unit can reach every village, dispatch them\n";
00661         full_dispatch(reachmap, moves);
00662         reachmap.clear();
00663         return;
00664     }
00665 
00666     // ***** ***** Find a square
00667     std::multimap<size_t /* villages_per_unit value*/, size_t /*villages_per_unit index*/>
00668         ::const_iterator src_itor =  unit_lookup.begin();
00669 
00670     while(src_itor != unit_lookup.end() && src_itor->first == 2) {
00671         
00672         for(std::multimap<size_t, size_t>::const_iterator 
00673                 dst_itor = unit_lookup.begin();
00674                 dst_itor != unit_lookup.end(); ++ dst_itor) {
00675 
00676             // avoid comparing us with ourselves.
00677             if(src_itor == dst_itor) {
00678                 continue;
00679             }
00680 
00681             std::vector<bool> result;
00682             std::transform(matrix[src_itor->second].begin(), matrix[src_itor->second].end(),
00683                 matrix[dst_itor->second].begin(),
00684                 std::back_inserter(result),
00685                 std::logical_and<bool>()
00686                 );
00687 
00688             size_t matched = std::count(result.begin(), result.end(), true);
00689             
00690             // we found a  solution, dispatch
00691             if(matched == 2) {
00692                 // Collect data
00693                 std::vector<bool>::iterator first = std::find(result.begin(), result.end(), true);
00694                 std::vector<bool>::iterator second = std::find(first + 1, result.end(), true);
00695 
00696                 const gamemap::location village1 = villages[first - result.begin()];
00697                 const gamemap::location village2 = villages[second - result.begin()];
00698 
00699                 const bool perfect = (src_itor->first == 2 && 
00700                     dst_itor->first == 2 && 
00701                     units_per_village[first - result.begin()] == 2 &&
00702                     units_per_village[second - result.begin()] == 2);
00703 
00704                 // Dispatch
00705                 DBG_AI << "Found a square.\nDispatched unit at " << units[src_itor->second]
00706                         << " to village " << village1 << '\n';
00707                 moves.push_back(std::make_pair(village1, units[src_itor->second]));
00708 
00709                 DBG_AI << "Dispatched unit at " << units[dst_itor->second]
00710                         << " to village " << village2 << '\n';
00711                 moves.push_back(std::make_pair(village2, units[dst_itor->second]));
00712 
00713                 // Remove the units
00714                 reachmap.erase(units[src_itor->second]);
00715                 reachmap.erase(units[dst_itor->second]);
00716                 
00717                 // Evaluate and start correct function.
00718                 if(perfect) {
00719                     // We did a perfect dispatch 2 units who could visit 2 villages.
00720                     // This means we didn't change the assertion for this funtions
00721                     // so call ourselves recursively, and finish afterwards.
00722                     DBG_AI << "Perfect dispatch, do complex again.\n";
00723                     dispatch_complex(reachmap, moves, village_count - 2);
00724                     return;
00725                 } else {
00726                     // We did a not perfect dispatch but we did modify things
00727                     // so restart dispatching.
00728                     DBG_AI << "NON Perfect dispatch, do dispatch again.\n";
00729                     remove_village(reachmap, moves, village1);
00730                     remove_village(reachmap, moves, village2);
00731                     dispatch(reachmap, moves);
00732                     return;
00733                 }
00734             }
00735         }
00736 
00737         ++src_itor;
00738     }
00739 
00740     // ***** ***** Do all permutations.
00741     // Now walk through all possible permutations
00742     // - test whether the suggestion is possible
00743     // - does it result in max_villages 
00744     //   - dispatch and ready
00745     // - is it's result better as the last best
00746     //   - store
00747     std::vector<std::pair<gamemap::location, gamemap::location> > best_result;
00748 
00749     // Bruteforcing all possible permutations can result in a slow game.
00750     // So there needs to be a balance between the best possible result and
00751     // not too slow. From the test (at the end of the file) a good number is
00752     // picked. In general we shouldn't reach this point too often if we do
00753     // there are a lot of villages which are unclaimed and a lot of units
00754     // to claim them.
00755     const size_t max_options = 8;
00756     if(unit_count >= max_options && village_count >= max_options) {
00757 
00758         DBG_AI << "Too many units " << unit_count << " and villages " 
00759             << village_count<<" found, evaluate only the first " 
00760             << max_options << " options;\n";
00761 
00762         std::vector<size_t> perm (max_options, 0);
00763         for(size_t i =0; i < max_options; ++i) {
00764             perm[i] = i;
00765         }
00766         while(std::next_permutation(perm.begin(), perm.end())) {
00767 
00768             // Get result for current permutation.
00769             std::vector<std::pair<gamemap::location,gamemap::location> > result;
00770             for(size_t u = 0; u < max_options; ++u) {
00771                 if(matrix[u][perm[u]]) {
00772                     result.push_back(std::make_pair(villages[perm[u]], units[u]));
00773 
00774                 }
00775             }
00776             if(result.size() == max_result) {
00777                 best_result.swap(result);
00778                 break;
00779             }
00780 
00781             if(result.size() > best_result.size()) {
00782                 best_result.swap(result);
00783             }
00784         }
00785         // End of loop no optimal found, assign the best
00786         std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
00787 
00788         // Clean up the reachmap for dispatched units.
00789         for(std::vector<std::pair<gamemap::location, gamemap::location> >::const_iterator 
00790                 itor = best_result.begin(); itor != best_result.end(); ++itor) {
00791             reachmap.erase(itor->second);
00792         }
00793 
00794         // Try to dispatch whatever is left
00795         dispatch(reachmap, moves);
00796         return;
00797 
00798     } else if(unit_count <= village_count) {
00799 
00800         DBG_AI << "Unit major\n";
00801 
00802         std::vector<size_t> perm (unit_count, 0);
00803         for(size_t i =0; i < unit_count; ++i) {
00804             perm[i] = i;
00805         }
00806         while(std::next_permutation(perm.begin(), perm.end())) {
00807             // Get result for current permutation.
00808             std::vector<std::pair<gamemap::location,gamemap::location> > result;
00809             for(size_t u = 0; u < unit_count; ++u) {
00810                 if(matrix[u][perm[u]]) {
00811                     result.push_back(std::make_pair(villages[perm[u]], units[u]));
00812 
00813                 }
00814             }
00815             if(result.size() == max_result) {
00816                 std::copy(result.begin(), result.end(), std::back_inserter(moves));
00817                 reachmap.clear();
00818                 return;
00819             }
00820 
00821             if(result.size() > best_result.size()) {
00822                 best_result.swap(result);
00823             }
00824         }
00825         // End of loop no optimal found, assign the best
00826         std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
00827 
00828         // clean up the reachmap we need to test whether the leader is still there
00829         // and if so remove him manually to get him dispatched.
00830         for(std::vector<std::pair<gamemap::location, gamemap::location> >::const_iterator 
00831                 itor = best_result.begin(); itor != best_result.end(); ++itor) {
00832             reachmap.erase(itor->second);
00833         }
00834         treachmap::iterator unit = reachmap.find(leader_loc);
00835         if(unit != reachmap.end()) {
00836             unit->second.clear();
00837             remove_unit(reachmap, moves, unit);
00838         }
00839         reachmap.clear();
00840 
00841     } else {
00842 
00843         DBG_AI << "Village major\n";
00844 
00845         std::vector<size_t> perm (village_count, 0);
00846         for(size_t i =0; i < village_count; ++i) {
00847             perm[i] = i;
00848         }
00849         while(std::next_permutation(perm.begin(), perm.end())) {
00850             // Get result for current permutation.
00851             std::vector<std::pair<gamemap::location,gamemap::location> > result;
00852             for(size_t v = 0; v < village_count; ++v) {
00853                 if(matrix[perm[v]][v]) {
00854                     result.push_back(std::make_pair(villages[v], units[perm[v]]));
00855 
00856                 }
00857             }
00858             if(result.size() == max_result) {
00859                 std::copy(result.begin(), result.end(), std::back_inserter(moves));
00860                 reachmap.clear();
00861                 return;
00862             }
00863 
00864             if(result.size() > best_result.size()) {
00865                 best_result.swap(result);
00866             }
00867         }
00868         // End of loop no optimal found, assigne the best
00869         std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
00870 
00871         // clean up the reachmap we need to test whether the leader is still there
00872         // and if so remove him manually to get him dispatched.
00873         for(std::vector<std::pair<gamemap::location, gamemap::location> >::const_iterator 
00874                 itor = best_result.begin(); itor != best_result.end(); ++itor) {
00875             reachmap.erase(itor->second);
00876         }
00877         treachmap::iterator unit = reachmap.find(leader_loc);
00878         if(unit != reachmap.end()) {
00879             unit->second.clear();
00880             remove_unit(reachmap, moves, unit);
00881         }
00882         reachmap.clear();
00883     }
00884 }
00885 
00886 static void full_dispatch(treachmap& reachmap, tmoves& moves)
00887 {
00888     treachmap::const_iterator itor = reachmap.begin();
00889     for(size_t i = 0; i < reachmap.size(); ++i, ++itor) { 
00890         DBG_AI << "Dispatched unit at " << itor->first
00891                 << " to village " << itor->second[i] << '\n';
00892         moves.push_back(std::make_pair(itor->second[i], itor->first));
00893     }
00894 }
00895 
00896 static void dump_reachmap(treachmap& reachmap)
00897 {
00898     if(!debug) {
00899         return;
00900     }
00901 
00902     for(treachmap::const_iterator itor = 
00903             reachmap.begin(); itor != reachmap.end(); ++itor) {
00904             
00905         std::cerr << "Reachlist for unit at " << itor->first;
00906 
00907         if(itor->second.empty()) {
00908             std::cerr << "\tNone";
00909         }
00910 
00911         for(std::vector<gamemap::location>::const_iterator 
00912                 v_itor = itor->second.begin();
00913                 v_itor != itor->second.end(); ++v_itor) {
00914 
00915             std::cerr << '\t' << *v_itor;
00916         }
00917         std::cerr << '\n';
00918 
00919     }
00920 }
00921 
00922 #if 0
00923 // small helper rule to test the matching rules
00924 // building rule
00925 //make ai_village.o &&  g++-3.3 -o ai_village about.o actions.o ai.o ai_dfool.o ai_attack.o ai_move.o ai_python.o ai_village.o animated_game.o attack_prediction.o config_adapter.o dialogs.o floating_textbox.o game_display.o game_events.o game_preferences.o game_preferences_display.o gamestatus.o generate_report.o generic_event.o halo.o help.o intro.o leader_list.o menu_events.o mouse_events.o multiplayer.o multiplayer_ui.o multiplayer_wait.o multiplayer_connect.o multiplayer_create.o multiplayer_lobby.o network.o network_worker.o pathfind.o playcampaign.o play_controller.o playmp_controller.o playsingle_controller.o playturn.o publish_campaign.o replay.o replay_controller.o sha1.o settings.o statistics.o team.o terrain_filter.o titlescreen.o tooltips.o unit.o unit_abilities.o unit_animation.o unit_display.o unit_frame.o unit_map.o unit_types.o upload_log.o variable.o widgets/combo.o widgets/scrollpane.o -L. -lwesnoth-core -lSDL_image -lSDL_mixer -lSDL_net  -L/usr/lib -lSDL -L/usr/lib -lpython2.4  -lfreetype -lz  -L/usr/lib -lfribidi libwesnoth.a -lboost_iostreams  -lX11 -L/usr/lib -R/usr/lib
00926 /* 
00927 // gcc-3.3 -O0
00928 Option count : 1 duration 0 ms
00929 Option count : 2 duration 0 ms
00930 Option count : 3 duration 0 ms
00931 Option count : 4 duration 1 ms
00932 Option count : 5 duration 1 ms
00933 Option count : 6 duration 3 ms
00934 Option count : 7 duration 16 ms
00935 Option count : 8 duration 208 ms
00936 Option count : 9 duration 1915 ms
00937 Option count : 10 duration 18424 ms
00938 
00939 // gcc-3.3 -O2
00940 Option count : 1 duration 0 ms
00941 Option count : 2 duration 0 ms
00942 Option count : 3 duration 0 ms
00943 Option count : 4 duration 0 ms
00944 Option count : 5 duration 1 ms
00945 Option count : 6 duration 0 ms
00946 Option count : 7 duration 3 ms
00947 Option count : 8 duration 41 ms
00948 Option count : 9 duration 374 ms
00949 Option count : 10 duration 3496 ms
00950 Option count : 11 duration 38862 ms
00951 
00952 // gcc-4.1 -O2
00953 Option count : 1 duration 0 ms
00954 Option count : 2 duration 0 ms
00955 Option count : 3 duration 0 ms
00956 Option count : 4 duration 0 ms
00957 Option count : 5 duration 0 ms
00958 Option count : 6 duration 1 ms
00959 Option count : 7 duration 2 ms
00960 Option count : 8 duration 26 ms
00961 Option count : 9 duration 261 ms
00962 Option count : 10 duration 2336 ms
00963 Option count : 11 duration 26684 ms
00964 
00965 */ 
00966 int main()
00967 {
00968     const size_t max_matrix = 100;
00969     std::vector</*unit*/std::vector</*village*/bool> > matrix(max_matrix, std::vector<bool>(max_matrix, false));
00970     std::vector<gamemap::location> villages(max_matrix);
00971     std::vector<gamemap::location> units(max_matrix);
00972 
00973     srand(10);
00974     for(size_t i = 0; i < max_matrix; ++i) {
00975         for(size_t j = 0; j < max_matrix; ++j) {
00976             matrix[i][j] = ((rand() % 3) == 0);
00977             villages[i] = gamemap::location(rand() % 100, rand() % 100);
00978             units[i] = gamemap::location(rand() % 100, rand() % 100);
00979         }
00980     }
00981 
00982     // Permutations for 0 are quite senseless.
00983     std::vector<std::pair<gamemap::location,gamemap::location> > best_result;
00984     for(size_t option = 1; option < max_matrix; ++option) {
00985         // Set up the permuation 
00986         std::vector<size_t> perm (option, 0);
00987         for(size_t i = 0; i < option; ++i) {
00988             perm[i] = i;
00989         }
00990 
00991         const int start = SDL_GetTicks();
00992         while(std::next_permutation(perm.begin(), perm.end())) {
00993 
00994             // Get result for current permutation.
00995             std::vector<std::pair<gamemap::location,gamemap::location> > result;
00996             for(size_t u = 0; u < option; ++u) {
00997                 if(matrix[u][perm[u]]) {
00998                     result.push_back(std::make_pair(villages[perm[u]], units[u]));
00999 
01000                 }
01001             }
01002             if(result.size() == option) {
01003                 best_result.swap(result);
01004                 break;
01005             }
01006 
01007             if(result.size() > best_result.size()) {
01008                 best_result.swap(result);
01009             }
01010         }
01011 
01012         // Don't use buffered output we _expect_ the user to kill the program.
01013         std::cerr << "Option count : " << option << " duration " << (SDL_GetTicks() - start) << " ms\n";
01014 
01015     }
01016 }
01017 
01018 #endif
01019 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs