ai/testing/ca_testing_move_to_targets.cpp

Go to the documentation of this file.
00001 /* $Id: ca_testing_move_to_targets.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2009 - 2012 by Yurii Chernyi <terraninfo@terraninfo.net>
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 as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Strategic movement routine, taken from default AI
00019  */
00020 
00021 #include "ca_testing_move_to_targets.hpp"
00022 
00023 #include "../composite/ai.hpp"
00024 #include "../actions.hpp"
00025 #include "../../foreach.hpp"
00026 #include "../../log.hpp"
00027 #include "../../map.hpp"
00028 #include "../../resources.hpp"
00029 #include "../../team.hpp"
00030 #include "../../terrain_filter.hpp"
00031 #include "../../pathfind/pathfind.hpp"
00032 
00033 #include <deque>
00034 
00035 namespace ai {
00036 
00037 namespace testing_ai_default {
00038 
00039 static lg::log_domain log_ai_testing_ca_testing_move_to_targets("ai/ca/testing_move_to_targets");
00040 #define DBG_AI LOG_STREAM(debug, log_ai_testing_ca_testing_move_to_targets)
00041 #define LOG_AI LOG_STREAM(info, log_ai_testing_ca_testing_move_to_targets)
00042 #define WRN_AI LOG_STREAM(warn, log_ai_testing_ca_testing_move_to_targets)
00043 #define ERR_AI LOG_STREAM(err, log_ai_testing_ca_testing_move_to_targets)
00044 
00045 
00046 
00047 struct move_cost_calculator : pathfind::cost_calculator
00048 {
00049     move_cost_calculator(const unit& u, const gamemap& map,
00050             const unit_map& units, const move_map& enemy_dstsrc)
00051       : unit_(u), map_(map), units_(units),
00052         enemy_dstsrc_(enemy_dstsrc),
00053         max_moves_(u.total_movement()),
00054         avoid_enemies_(u.usage() == "scout")
00055     {}
00056 
00057     double cost(const map_location& loc, const double) const
00058     {
00059         const t_translation::t_terrain terrain = map_[loc];
00060 
00061         const double move_cost = unit_.movement_cost(terrain);
00062 
00063         if(move_cost > max_moves_) // impassable
00064             return getNoPathValue();
00065 
00066         double res = move_cost;
00067         if(avoid_enemies_){
00068             res *= 1.0 + enemy_dstsrc_.count(loc);
00069         }
00070 
00071         //if there is a unit (even a friendly one) on this tile, we increase the cost to
00072         //try discourage going through units, to thwart the 'single file effect'
00073         if (units_.count(loc))
00074             res *= 4.0;
00075 
00076         return res;
00077     }
00078 
00079 private:
00080     const unit& unit_;
00081     const gamemap& map_;
00082     const unit_map& units_;
00083     const move_map& enemy_dstsrc_;
00084     const int max_moves_;
00085     const bool avoid_enemies_;
00086 };
00087 
00088 
00089 class remove_wrong_targets {
00090 public:
00091     remove_wrong_targets(const readonly_context &context)
00092         :avoid_(context.get_avoid()), map_(*resources::game_map)
00093     {
00094     }
00095 
00096 bool operator()(const target &t){
00097     if (!map_.on_board(t.loc)) {
00098         DBG_AI << "removing target "<< t.loc << " due to it not on_board" << std::endl;
00099         return true;
00100     }
00101 
00102     if (t.value<=0) {
00103         DBG_AI << "removing target "<< t.loc << " due to value<=0" << std::endl;
00104         return true;
00105     }
00106 
00107     if (avoid_.match(t.loc)) {
00108         DBG_AI << "removing target "<< t.loc << " due to 'avoid' match" << std::endl;
00109         return true;
00110     }
00111 
00112     return false;
00113 }
00114 private:
00115     const terrain_filter &avoid_;
00116     const gamemap &map_;
00117 
00118 };
00119 
00120 testing_move_to_targets_phase::testing_move_to_targets_phase( rca_context &context, const config &cfg )
00121     : candidate_action(context,cfg)
00122 {
00123 }
00124 
00125 
00126 testing_move_to_targets_phase::~testing_move_to_targets_phase()
00127 {
00128 }
00129 
00130 
00131 double testing_move_to_targets_phase::evaluate()
00132 {
00133     return get_score();
00134 }
00135 
00136 
00137 void testing_move_to_targets_phase::execute()
00138 {
00139     unit_map::const_iterator leader = resources::units->find_leader(get_side());
00140     LOG_AI << "finding targets...\n";
00141     std::vector<target> targets;
00142     for(;;) {
00143         if(targets.empty()) {
00144             targets = find_targets(get_enemy_dstsrc());
00145             targets.insert(targets.end(),additional_targets().begin(),
00146                        additional_targets().end());
00147             LOG_AI << "Found " << targets.size() << " targets\n";
00148             if(targets.empty()) {
00149                 break;
00150             }
00151         }
00152 
00153         targets.erase( std::remove_if(targets.begin(),targets.end(),remove_wrong_targets(*this)), targets.end() );
00154 
00155         if(targets.empty()) {
00156             break;
00157         }
00158 
00159         LOG_AI << "choosing move with " << targets.size() << " targets\n";
00160         std::pair<map_location,map_location> move = choose_move(targets, get_srcdst(),
00161                                 get_dstsrc(), get_enemy_dstsrc());
00162         LOG_AI << "choose_move ends with " << targets.size() << " targets\n";
00163 
00164         for(std::vector<target>::const_iterator ittg = targets.begin();
00165                 ittg != targets.end(); ++ittg) {
00166             assert(resources::game_map->on_board(ittg->loc));
00167         }
00168 
00169         if(move.first.valid() == false || move.second.valid() == false) {
00170             break;
00171         }
00172 
00173         assert (resources::game_map->on_board(move.first)
00174             && resources::game_map->on_board(move.second));
00175 
00176         LOG_AI << "move: " << move.first << " -> " << move.second << '\n';
00177 
00178         move_result_ptr move_ptr = execute_move_action(move.first,move.second,true);
00179         if(!move_ptr->is_ok()) {
00180             WRN_AI << "unexpected outcome of move"<<std::endl;
00181             break;
00182         }
00183     }
00184 }
00185 
00186 
00187 
00188 
00189 
00190 // structure storing the maximal possible rating of a target
00191 struct rated_target{
00192     rated_target(const std::vector<target>::iterator& t, double r) : tg(t), max_rating(r) {};
00193     std::vector<target>::iterator tg;
00194     double max_rating;
00195 };
00196 
00197 // compare maximal possible rating of targets
00198 // we can be smarter about the equal case, but keep old behavior for the moment
00199 struct rated_target_comparer {
00200     bool operator()(const rated_target& a, const rated_target& b) const {
00201         return a.max_rating > b.max_rating;
00202     }
00203 };
00204 
00205 
00206 double testing_move_to_targets_phase::rate_target(const target& tg, const unit_map::iterator& u,
00207             const move_map& dstsrc, const move_map& enemy_dstsrc,
00208             const pathfind::plain_route& rt)
00209 {
00210     double move_cost = rt.move_cost;
00211 
00212     if(move_cost > 0) {
00213         // if this unit can move to that location this turn, it has a very very low cost
00214         typedef std::multimap<map_location,map_location>::const_iterator multimapItor;
00215         std::pair<multimapItor,multimapItor> locRange = dstsrc.equal_range(tg.loc);
00216         while (locRange.first != locRange.second) {
00217             if (locRange.first->second == u->get_location()) {
00218                 move_cost = 0;
00219                 break;
00220             }
00221             ++locRange.first;
00222         }
00223     }
00224 
00225     double rating = tg.value;
00226 
00227     if(rating == 0)
00228         return rating; // all following operations are only multiplications of 0
00229 
00230     // far target have a lower rating
00231     if(move_cost > 0) {
00232         rating /= move_cost;
00233     }
00234 
00235     //for 'support' targets, they are rated much higher if we can get there within two turns,
00236     //otherwise they are worthless to go for at all.
00237     if(tg.type == target::SUPPORT) {
00238         if (move_cost <= u->movement_left() * 2) {
00239             rating *= 10.0;
00240         } else {
00241             rating = 0.0;
00242             return rating;
00243         }
00244     }
00245 
00246     //scouts do not like encountering enemies on their paths
00247     if (u->usage() == "scout") {
00248         //scouts get a bonus for going after villages
00249         if(tg.type == target::VILLAGE) {
00250                 rating *= get_scout_village_targeting();
00251         }
00252 
00253         std::set<map_location> enemies_guarding;
00254         enemies_along_path(rt.steps,enemy_dstsrc,enemies_guarding);
00255         // note that an empty route means no guardian and thus optimal rating
00256 
00257         if(enemies_guarding.size() > 1) {
00258             rating /= enemies_guarding.size();
00259         } else {
00260             //scouts who can travel on their route without coming in range of many enemies
00261             //get a massive bonus, so that they can be processed first, and avoid getting
00262             //bogged down in lots of grouping
00263             rating *= 100;
00264         }
00265     }
00266 
00267     return rating;
00268 }
00269 
00270 
00271 
00272 std::pair<map_location,map_location> testing_move_to_targets_phase::choose_move(std::vector<target>& targets, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc)
00273 {
00274     log_scope2(log_ai_testing_ca_testing_move_to_targets, "choosing move");
00275 
00276     raise_user_interact();
00277     unit_map &units_ = *resources::units;
00278     gamemap &map_ = *resources::game_map;
00279 
00280     unit_map::iterator u;
00281 
00282     //find the first eligible unit
00283     for(u = units_.begin(); u != units_.end(); ++u) {
00284         if (!(u->side() != get_side() || u->can_recruit() || u->movement_left() <= 0 || u->incapacitated())) {
00285             break;
00286         }
00287     }
00288 
00289     if(u == units_.end()) {
00290         LOG_AI  << "no eligible units found\n";
00291         return std::pair<map_location,map_location>();
00292     }
00293 
00294     //guardian units stay put
00295     if (u->get_state("guardian")) {
00296         LOG_AI << u->type_id() << " is guardian, staying still\n";
00297         return std::make_pair(u->get_location(), u->get_location());
00298     }
00299 
00300     const pathfind::plain_route dummy_route;
00301     assert(dummy_route.steps.empty() && dummy_route.move_cost == 0);
00302 
00303     // We will sort all targets by a quick maximal possible rating,
00304     // so we will be able to start real work by the most promising ones
00305     // and if its real value is better than other maximal values
00306     // then we can skip them.
00307 
00308     std::vector<rated_target> rated_targets;
00309     for(std::vector<target>::iterator tg = targets.begin(); tg != targets.end(); ++tg) {
00310         // passing a dummy route to have the maximal rating
00311         double max_rating = rate_target(*tg, u, dstsrc, enemy_dstsrc, dummy_route);
00312         rated_targets.push_back( rated_target(tg, max_rating) );
00313     }
00314 
00315     //use stable_sort for the moment to preserve old AI behavior
00316     std::stable_sort(rated_targets.begin(), rated_targets.end(), rated_target_comparer());
00317 
00318     const move_cost_calculator cost_calc(*u, map_, units_, enemy_dstsrc);
00319 
00320     pathfind::plain_route best_route;
00321     unit_map::iterator best = units_.end();
00322     double best_rating = -1.0;
00323 
00324     std::vector<rated_target>::iterator best_rated_target = rated_targets.end();
00325 
00326     std::vector<rated_target>::iterator rated_tg = rated_targets.begin();
00327 
00328     for(; rated_tg != rated_targets.end(); ++rated_tg) {
00329         const target& tg = *(rated_tg->tg);
00330 
00331         LOG_AI << "Considering target at: " << tg.loc <<"\n";
00332         assert(map_.on_board(tg.loc));
00333 
00334         raise_user_interact();
00335 
00336         // locStopValue controls how quickly we give up on the A* search, due
00337         // to it seeming futile. Be very cautious about changing this value,
00338         // as it can cause the AI to give up on searches and just do nothing.
00339         const double locStopValue = 500.0;
00340         pathfind::plain_route real_route = a_star_search(u->get_location(), tg.loc, locStopValue, &cost_calc, map_.w(), map_.h());
00341 
00342         if(real_route.steps.empty()) {
00343             LOG_AI << "Can't reach target: " << locStopValue << " = " << tg.value << "/" << best_rating << "\n";
00344             continue;
00345         }
00346 
00347         double real_rating = rate_target(tg, u, dstsrc, enemy_dstsrc, real_route);
00348 
00349         LOG_AI << tg.value << "/" << real_route.move_cost << " = " << real_rating << "\n";
00350 
00351         if(real_rating > best_rating){
00352             best_rating = real_rating;
00353             best_rated_target = rated_tg;
00354             best_route = real_route;
00355             best = u;
00356             //prevent divivion by zero
00357             //FIXME: stupid, should fix it at the division
00358             if(best_rating == 0)
00359                 best_rating = 0.000000001;
00360 
00361             // if already higher than the maximal values of the next ratings
00362             // (which are sorted, so only need to check the next one)
00363             // then we have found the best target.
00364             if(rated_tg+1 != rated_targets.end() && best_rating >= (rated_tg+1)->max_rating)
00365                 break;
00366         }
00367     }
00368 
00369     LOG_AI << "choose target...\n";
00370 
00371     if(best_rated_target == rated_targets.end()) {
00372         LOG_AI << "no eligible targets found for unit at " << u->get_location() << std::endl;
00373         return std::make_pair(u->get_location(), u->get_location());
00374     }
00375 
00376     assert(best_rating >= 0);
00377     std::vector<target>::iterator best_target = best_rated_target->tg;
00378 
00379     //if we have the 'simple_targeting' flag set, then we don't
00380     //see if any other units can put a better bid forward for this
00381     //target
00382     bool simple_targeting = get_simple_targeting();
00383 
00384     if(simple_targeting == false) {
00385         LOG_AI << "complex targeting...\n";
00386         //now see if any other unit can put a better bid forward
00387         for(++u; u != units_.end(); ++u) {
00388             if (u->side() != get_side() || u->can_recruit() ||
00389                 u->movement_left() <= 0 || u->get_state("guardian") ||
00390                 u->incapacitated())
00391             {
00392                 continue;
00393             }
00394 
00395             raise_user_interact();
00396 
00397             const move_cost_calculator calc(*u, map_, units_, enemy_dstsrc);
00398 
00399             ///@todo 1.9: lower this value for perf,
00400             // but best_rating is too big for scout and support
00401             // which give a too small locStopValue
00402             // so keep costy A* for the moment.
00403             //const double locStopValue = std::min(best_target->value / best_rating, (double) 100.0);
00404 
00405             const double locStopValue = 500.0;
00406             pathfind::plain_route cur_route = pathfind::a_star_search(u->get_location(), best_target->loc, locStopValue, &calc, map_.w(), map_.h());
00407 
00408             if(cur_route.steps.empty()) {
00409                 continue;
00410             }
00411 
00412             double rating = rate_target(*best_target, u, dstsrc, enemy_dstsrc, cur_route);
00413 
00414             if(best == units_.end() || rating > best_rating) {
00415                 best_rating = rating;
00416                 best = u;
00417                 best_route = cur_route;
00418             }
00419         }
00420 
00421         LOG_AI << "done complex targeting...\n";
00422     } else {
00423         u = units_.end();
00424     }
00425 
00426     LOG_AI << "best unit: " << best->get_location() << '\n';
00427 
00428     assert(best_target != targets.end());
00429 
00430     //if our target is a position to support, then we
00431     //see if we can move to a position in support of this target
00432     if(best_target->type == target::SUPPORT) {
00433         LOG_AI << "support...\n";
00434 
00435         std::vector<map_location> locs;
00436         access_points(srcdst, best->get_location(), best_target->loc, locs);
00437 
00438         if(locs.empty() == false) {
00439             LOG_AI << "supporting unit at " << best_target->loc.x + 1 << "," << best_target->loc.y + 1 << "\n";
00440             map_location best_loc;
00441             int best_defense = 0;
00442             double best_vulnerability = 0.0;
00443 
00444             for(std::vector<map_location>::const_iterator i = locs.begin(); i != locs.end(); ++i) {
00445                 const int defense = best->defense_modifier(map_.get_terrain(*i));
00446                 //FIXME: suokko multiplied by 10 * get_caution(). ?
00447                 const double vulnerability = power_projection(*i,enemy_dstsrc);
00448 
00449                 if(best_loc.valid() == false || defense < best_defense || (defense == best_defense && vulnerability < best_vulnerability)) {
00450                     best_loc = *i;
00451                     best_defense = defense;
00452                     best_vulnerability = vulnerability;
00453                 }
00454             }
00455 
00456             LOG_AI << "returning support...\n";
00457             return std::make_pair(best->get_location(), best_loc);
00458         }
00459     }
00460 
00461     std::map<map_location,pathfind::paths> dummy_possible_moves;
00462     move_map fullmove_srcdst;
00463     move_map fullmove_dstsrc;
00464     calculate_possible_moves(dummy_possible_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
00465 
00466     bool dangerous = false;
00467 
00468     if(get_grouping() != "no") {
00469         LOG_AI << "grouping...\n";
00470         const unit_map::const_iterator unit_at_target = units_.find(best_target->loc);
00471         int movement = best->movement_left();
00472 
00473         const bool defensive_grouping = get_grouping() == "defensive";
00474 
00475         //we stop and consider whether the route to this
00476         //target is dangerous, and whether we need to group some units to move in unison toward the target
00477         //if any point along the path is too dangerous for our single unit, then we hold back
00478         for(std::vector<map_location>::const_iterator i = best_route.steps.begin(); i != best_route.steps.end() && movement > 0; ++i) {
00479 
00480             //FIXME: suokko multiplied by 10 * get_caution(). ?
00481             const double threat = power_projection(*i,enemy_dstsrc);
00482             //FIXME: sukko doubled the power-projection them in the second test.  ?
00483             if ((threat >= best->hitpoints() && threat > power_projection(*i,fullmove_dstsrc)) ||
00484                (i+1 >= best_route.steps.end()-1 && unit_at_target != units_.end() && current_team().is_enemy(unit_at_target->side()))) {
00485                 dangerous = true;
00486                 break;
00487             }
00488 
00489             if(!defensive_grouping) {
00490                 movement -= best->movement_cost(map_.get_terrain(*i));
00491             }
00492         }
00493 
00494         LOG_AI << "done grouping...\n";
00495     }
00496 
00497     if(dangerous) {
00498         LOG_AI << "dangerous path\n";
00499         std::set<map_location> group, enemies;
00500         const map_location dst = form_group(best_route.steps,dstsrc,group);
00501         enemies_along_path(best_route.steps,enemy_dstsrc,enemies);
00502 
00503         const double our_strength = compare_groups(group,enemies,best_route.steps);
00504 
00505         if(our_strength > 0.5 + get_caution()) {
00506             LOG_AI << "moving group\n";
00507             const bool res = move_group(dst,best_route.steps,group);
00508             if(res) {
00509                 return std::pair<map_location,map_location>(map_location(1,1),map_location());
00510             } else {
00511                 LOG_AI << "group didn't move " << group.size() << "\n";
00512 
00513                 //the group didn't move, so end the first unit in the group's turn, to prevent an infinite loop
00514                 return std::make_pair(best->get_location(), best->get_location());
00515 
00516             }
00517         } else {
00518             LOG_AI << "massing to attack " << best_target->loc.x + 1 << "," << best_target->loc.y + 1
00519                 << " " << our_strength << "\n";
00520 
00521             const double value = best_target->value;
00522             const map_location target_loc = best_target->loc;
00523             const map_location loc = best->get_location();
00524             const unit& un = *best;
00525 
00526             targets.erase(best_target);
00527 
00528             //find the best location to mass units at for an attack on the enemies
00529             map_location best_loc;
00530             double best_threat = 0.0;
00531             int best_distance = 0;
00532 
00533             const double max_acceptable_threat = un.hitpoints()/4;
00534 
00535             std::set<map_location> mass_locations;
00536 
00537             const std::pair<move_map::const_iterator,move_map::const_iterator> itors = srcdst.equal_range(loc);
00538             for(move_map::const_iterator i = itors.first; i != itors.second; ++i) {
00539                 const int distance = distance_between(target_loc,i->second);
00540                 const int defense = un.defense_modifier(map_.get_terrain(i->second));
00541                 //FIXME: suokko multiplied by 10 * get_caution(). ?
00542                 const double threat = (power_projection(i->second,enemy_dstsrc)*defense)/100;
00543 
00544                 if(best_loc.valid() == false || (threat < std::max<double>(best_threat,max_acceptable_threat) && distance < best_distance)) {
00545                     best_loc = i->second;
00546                     best_threat = threat;
00547                     best_distance = distance;
00548                 }
00549 
00550                 if(threat < max_acceptable_threat) {
00551                     mass_locations.insert(i->second);
00552                 }
00553             }
00554 
00555             for(std::set<map_location>::const_iterator j = mass_locations.begin(); j != mass_locations.end(); ++j) {
00556                 if(*j != best_loc && distance_between(*j,best_loc) < 3) {
00557                     LOG_AI << "found mass-to-attack target... " << *j << " with value: " << value*4.0 << "\n";
00558                     targets.push_back(target(*j,value*4.0,target::MASS));
00559                     best_target = targets.end() - 1;
00560                 }
00561             }
00562 
00563             return std::pair<map_location,map_location>(loc,best_loc);
00564         }
00565     }
00566 
00567     for(std::vector<map_location>::reverse_iterator ri =
00568         best_route.steps.rbegin(); ri != best_route.steps.rend(); ++ri) {
00569 
00570         //this is set to 'true' if we are hesitant to proceed because of enemy units,
00571         //to rally troops around us.
00572         bool is_dangerous = false;
00573 
00574         typedef std::multimap<map_location,map_location>::const_iterator Itor;
00575         std::pair<Itor,Itor> its = dstsrc.equal_range(*ri);
00576         while(its.first != its.second) {
00577             if (its.first->second == best->get_location()) {
00578                 if(!should_retreat(its.first->first,best,fullmove_srcdst,fullmove_dstsrc,enemy_dstsrc,
00579                                    get_caution())) {
00580                     double value = best_target->value - best->cost() / 20.0;
00581 
00582                     if(value > 0.0 && best_target->type != target::MASS) {
00583                         //there are enemies ahead. Rally troops around us to
00584                         //try to take the target
00585                         if(is_dangerous) {
00586                             LOG_AI << "found reinforcement target... " << its.first->first << " with value: " << value*2.0 << "\n";
00587                             targets.push_back(target(its.first->first,value*2.0,target::BATTLE_AID));
00588                         }
00589 
00590                         best_target->value = value;
00591                     } else {
00592                         targets.erase(best_target);
00593                     }
00594 
00595                     LOG_AI << "Moving to " << its.first->first.x + 1 << "," << its.first->first.y + 1 << "\n";
00596 
00597                     return std::pair<map_location,map_location>(its.first->second,its.first->first);
00598                 } else {
00599                     LOG_AI << "dangerous!\n";
00600                     is_dangerous = true;
00601                 }
00602             }
00603 
00604             ++its.first;
00605         }
00606     }
00607 
00608     if(best != units_.end()) {
00609         LOG_AI << "Could not make good move, staying still\n";
00610 
00611         //this sounds like the road ahead might be dangerous, and that's why we don't advance.
00612         //create this as a target, attempting to rally units around
00613         targets.push_back(target(best->get_location(), best_target->value));
00614         best_target = targets.end() - 1;
00615         return std::make_pair(best->get_location(), best->get_location());
00616     }
00617 
00618     LOG_AI << "Could not find anywhere to move!\n";
00619     return std::pair<map_location,map_location>();
00620 }
00621 
00622 void testing_move_to_targets_phase::access_points(const move_map& srcdst, const map_location& u, const map_location& dst, std::vector<map_location>& out)
00623 {
00624     unit_map &units_ = *resources::units;
00625     gamemap &map_ = *resources::game_map;
00626     const unit_map::const_iterator u_it = units_.find(u);
00627     if(u_it == units_.end()) {
00628         return;
00629     }
00630 
00631     // unit_map single_unit(u_it->first, u_it->second);
00632 
00633     const std::pair<move_map::const_iterator,move_map::const_iterator> locs = srcdst.equal_range(u);
00634     for(move_map::const_iterator i = locs.first; i != locs.second; ++i) {
00635         const map_location& loc = i->second;
00636         if (int(distance_between(loc,dst)) <= u_it->total_movement()) {
00637             pathfind::shortest_path_calculator calc(*u_it, current_team(), units_, *resources::teams, map_);
00638             pathfind::plain_route rt = a_star_search(loc, dst, u_it->total_movement(), &calc, map_.w(), map_.h());
00639             if(rt.steps.empty() == false) {
00640                 out.push_back(loc);
00641             }
00642         }
00643     }
00644 }
00645 
00646 
00647 double testing_move_to_targets_phase::compare_groups(const std::set<map_location>& our_group, const std::set<map_location>& their_group, const std::vector<map_location>& battlefield) const
00648 {
00649     const double a = rate_group(our_group,battlefield);
00650     const double b = std::max<double>(rate_group(their_group,battlefield),0.01);
00651     return a/b;
00652 }
00653 
00654 
00655 void testing_move_to_targets_phase::enemies_along_path(const std::vector<map_location>& route, const move_map& dstsrc, std::set<map_location>& res)
00656 {
00657     for(std::vector<map_location>::const_iterator i = route.begin(); i != route.end(); ++i) {
00658         map_location adj[6];
00659         get_adjacent_tiles(*i,adj);
00660         for(size_t n = 0; n != 6; ++n) {
00661             const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(adj[n]);
00662             for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
00663                 res.insert(j->second);
00664             }
00665         }
00666     }
00667 }
00668 
00669 
00670 map_location testing_move_to_targets_phase::form_group(const std::vector<map_location>& route, const move_map& dstsrc, std::set<map_location>& res)
00671 {
00672     unit_map &units_ = *resources::units;
00673     if(route.empty()) {
00674         return map_location();
00675     }
00676 
00677     std::vector<map_location>::const_iterator i;
00678     for(i = route.begin(); i != route.end(); ++i) {
00679         if(units_.count(*i) > 0) {
00680             continue;
00681         }
00682 
00683         size_t n = 0, nunits = res.size();
00684 
00685         const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*i);
00686         for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
00687             if(res.count(j->second) != 0) {
00688                 ++n;
00689             } else {
00690                 const unit_map::const_iterator un = units_.find(j->second);
00691                 if(un == units_.end() || un->can_recruit() || un->movement_left() < un->total_movement()) {
00692                     continue;
00693                 }
00694 
00695                 res.insert(j->second);
00696             }
00697         }
00698 
00699         //if not all our units can reach this position.
00700         if(n < nunits) {
00701             break;
00702         }
00703     }
00704 
00705     if(i != route.begin()) {
00706         --i;
00707     }
00708 
00709     return *i;
00710 }
00711 
00712 
00713 bool testing_move_to_targets_phase::move_group(const map_location& dst, const std::vector<map_location>& route, const std::set<map_location>& units)
00714 {
00715     unit_map &units_ = *resources::units;
00716     gamemap &map_ = *resources::game_map;
00717 
00718     const std::vector<map_location>::const_iterator itor = std::find(route.begin(),route.end(),dst);
00719     if(itor == route.end()) {
00720         return false;
00721     }
00722 
00723     LOG_AI << "group has " << units.size() << " members\n";
00724 
00725     map_location next;
00726 
00727     size_t direction = 0;
00728 
00729     //find the direction the group is moving in
00730     if(itor+1 != route.end()) {
00731         next = *(itor+1);
00732     } else if(itor != route.begin()) {
00733         next = *(itor-1);
00734     }
00735 
00736     if(next.valid()) {
00737         map_location adj[6];
00738         get_adjacent_tiles(dst,adj);
00739 
00740         direction = std::find(adj,adj+6,next) - adj;
00741     }
00742 
00743     std::deque<map_location> preferred_moves;
00744     preferred_moves.push_back(dst);
00745 
00746     std::map<map_location,pathfind::paths> possible_moves;
00747     move_map srcdst, dstsrc;
00748     calculate_possible_moves(possible_moves,srcdst,dstsrc,false);
00749 
00750     bool gamestate_changed = false;
00751 
00752     for(std::set<map_location>::const_iterator i = units.begin(); i != units.end(); ++i) {
00753         const unit_map::const_iterator un = units_.find(*i);
00754         if(un == units_.end()) {
00755             continue;
00756         }
00757 
00758         map_location best_loc;
00759         int best_defense = -1;
00760         for(std::deque<map_location>::const_iterator j = preferred_moves.begin(); j != preferred_moves.end(); ++j) {
00761             if(units_.count(*j)) {
00762                 continue;
00763             }
00764 
00765             const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*j);
00766             move_map::const_iterator m;
00767             for(m = itors.first; m != itors.second; ++m) {
00768                 if(m->second == *i) {
00769                     break;
00770                 }
00771             }
00772 
00773             if(m == itors.second) {
00774                 continue;
00775             }
00776 
00777             int defense = un->defense_modifier(map_.get_terrain(*j));
00778             if(best_loc.valid() == false || defense < best_defense) {
00779                 best_loc = *j;
00780                 best_defense = defense;
00781             }
00782         }
00783 
00784         if(best_loc.valid()) {
00785             move_result_ptr move_res = execute_move_action(*i,best_loc);
00786             gamestate_changed |= move_res->is_gamestate_changed();
00787 
00788 
00789             //if we were ambushed or something went wrong,  abort the group's movement.
00790             if (!move_res->is_ok()) {
00791                 return gamestate_changed;
00792             }
00793 
00794             preferred_moves.erase(std::find(preferred_moves.begin(),preferred_moves.end(),best_loc));
00795 
00796             //find locations that are 'perpendicular' to the direction of movement for further units to move to.
00797             map_location adj[6];
00798             get_adjacent_tiles(best_loc,adj);
00799             for(size_t n = 0; n != 6; ++n) {
00800                 if(n != direction && ((n+3)%6) != direction && map_.on_board(adj[n]) &&
00801                    units_.count(adj[n]) == 0 && std::count(preferred_moves.begin(),preferred_moves.end(),adj[n]) == 0) {
00802                     preferred_moves.push_front(adj[n]);
00803                     LOG_AI << "added moves: " << adj[n].x + 1 << "," << adj[n].y + 1 << "\n";
00804                 }
00805             }
00806         } else {
00807             LOG_AI << "Could not move group member to any of " << preferred_moves.size() << " locations\n";
00808         }
00809     }
00810 
00811     return gamestate_changed;
00812 }
00813 
00814 
00815 double testing_move_to_targets_phase::rate_group(const std::set<map_location>& group, const std::vector<map_location>& battlefield) const
00816 {
00817     unit_map &units_ = *resources::units;
00818     gamemap &map_ = *resources::game_map;
00819 
00820     double strength = 0.0;
00821     for(std::set<map_location>::const_iterator i = group.begin(); i != group.end(); ++i) {
00822         const unit_map::const_iterator u = units_.find(*i);
00823         if(u == units_.end()) {
00824             continue;
00825         }
00826 
00827         const unit &un = *u;
00828 
00829         int defense = 0;
00830         for(std::vector<map_location>::const_iterator j = battlefield.begin(); j != battlefield.end(); ++j) {
00831             defense += un.defense_modifier(map_.get_terrain(*j));
00832         }
00833 
00834         defense /= battlefield.size();
00835 
00836         int best_attack = 0;
00837         const std::vector<attack_type>& attacks = un.attacks();
00838         for(std::vector<attack_type>::const_iterator a = attacks.begin(); a != attacks.end(); ++a) {
00839             const int strength = a->num_attacks()*a->damage();
00840             best_attack = std::max<int>(strength,best_attack);
00841         }
00842 
00843         const int rating = (defense*best_attack*un.hitpoints())/(100*un.max_hitpoints());
00844         strength += double(rating);
00845     }
00846 
00847     return strength;
00848 }
00849 
00850 
00851 
00852 bool testing_move_to_targets_phase::should_retreat(const map_location& loc, const unit_map::const_iterator& un,
00853         const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc,
00854         double caution)
00855 {
00856     if(caution <= 0.0) {
00857         return false;
00858     }
00859 
00860     double optimal_terrain = best_defensive_position(un->get_location(), dstsrc,
00861             srcdst, enemy_dstsrc).chance_to_hit/100.0;
00862     const double proposed_terrain =
00863         un->defense_modifier(resources::game_map->get_terrain(loc))/100.0;
00864 
00865     // The 'exposure' is the additional % chance to hit
00866     // this unit receives from being on a sub-optimal defensive terrain.
00867     const double exposure = proposed_terrain - optimal_terrain;
00868 
00869     const double our_power = power_projection(loc,dstsrc);
00870     const double their_power = power_projection(loc,enemy_dstsrc);
00871     return caution*their_power*(1.0+exposure) > our_power;
00872 }
00873 
00874 } // end of namespace testing_ai_default
00875 
00876 } // end of namespace ai
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:33 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs