ai/testing/ca.cpp

Go to the documentation of this file.
00001 /* $Id: ca.cpp 53551 2012-03-18 00:19:09Z anonymissimus $ */
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  * Default AI (Testing)
00018  * @file
00019  */
00020 
00021 #include "ca.hpp"
00022 #include "../actions.hpp"
00023 #include "../manager.hpp"
00024 #include "../composite/engine.hpp"
00025 #include "../composite/rca.hpp"
00026 #include "../composite/stage.hpp"
00027 #include "../../gamestatus.hpp"
00028 #include "../../foreach.hpp"
00029 #include "../../log.hpp"
00030 #include "../../map.hpp"
00031 #include "../../resources.hpp"
00032 #include "../../team.hpp"
00033 #include "../../wml_exception.hpp"
00034 #include "../../pathfind/pathfind.hpp"
00035 
00036 
00037 #include <numeric>
00038 
00039 static lg::log_domain log_ai_testing_ai_default("ai/ca/testing_ai_default");
00040 #define DBG_AI_TESTING_AI_DEFAULT LOG_STREAM(debug, log_ai_testing_ai_default)
00041 #define LOG_AI_TESTING_AI_DEFAULT LOG_STREAM(info, log_ai_testing_ai_default)
00042 #define WRN_AI_TESTING_AI_DEFAULT LOG_STREAM(warn, log_ai_testing_ai_default)
00043 #define ERR_AI_TESTING_AI_DEFAULT LOG_STREAM(err, log_ai_testing_ai_default)
00044 
00045 
00046 namespace ai {
00047 
00048 namespace testing_ai_default {
00049 
00050 //==============================================================
00051 
00052 goto_phase::goto_phase( rca_context &context, const config &cfg )
00053     : candidate_action(context,cfg)
00054     , move_()
00055 {
00056 }
00057 
00058 goto_phase::~goto_phase()
00059 {
00060 }
00061 
00062 double goto_phase::evaluate()
00063 {
00064     // Execute goto-movements - first collect gotos in a list
00065     std::vector<map_location> gotos;
00066     unit_map &units_ = *resources::units;
00067     gamemap &map_ = *resources::game_map;
00068 
00069     for(unit_map::iterator ui = units_.begin(); ui != units_.end(); ++ui) {
00070         if (ui->get_goto() == ui->get_location()) {
00071             ui->set_goto(map_location());
00072         } else if (ui->side() == get_side() && map_.on_board(ui->get_goto())) {
00073             gotos.push_back(ui->get_location());
00074         }
00075     }
00076 
00077     for(std::vector<map_location>::const_iterator g = gotos.begin(); g != gotos.end(); ++g) {
00078         unit_map::const_iterator ui = units_.find(*g);
00079         // passive_leader: never moves or attacks
00080         if(ui->can_recruit() && get_passive_leader() && !get_passive_leader_shares_keep()){
00081             continue;//@todo: only bail out if goto is on keep
00082         }
00083         // end of passive_leader
00084         int closest_distance = -1;
00085         std::pair<map_location,map_location> closest_move;
00086         for(move_map::const_iterator i = get_dstsrc().begin(); i != get_dstsrc().end(); ++i) {
00087             if(i->second != ui->get_location()) {
00088                 continue;
00089             }
00090             int distance = distance_between(i->first,ui->get_goto());
00091             if(closest_distance == -1 || distance < closest_distance) {
00092                 closest_distance = distance;
00093                 closest_move = *i;
00094             }
00095         }
00096 
00097         if(closest_distance != -1) {
00098             move_ = check_move_action(ui->get_location(), closest_move.first);
00099             if (move_->is_ok()) {
00100                 return get_score();
00101             }
00102         }
00103     }
00104 
00105     return BAD_SCORE;
00106 }
00107 
00108 void goto_phase::execute()
00109 {
00110     if (!move_) {
00111         return;
00112     }
00113 
00114     move_->execute();
00115     if (!move_->is_ok()){
00116         LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
00117     }
00118 }
00119 
00120 //==============================================================
00121 
00122 aspect_recruitment_phase::aspect_recruitment_phase( rca_context &context, const config &cfg )
00123     : candidate_action(context,cfg)
00124 {
00125 }
00126 
00127 
00128 aspect_recruitment_phase::~aspect_recruitment_phase()
00129 {
00130 }
00131 
00132 double aspect_recruitment_phase::evaluate()
00133 {
00134     const unit_map::const_iterator leader = resources::units->find_leader(get_side());
00135     if(leader == resources::units->end()) {
00136         return BAD_SCORE;
00137     }
00138     if (!resources::game_map->is_keep(leader->get_location())) {
00139         return BAD_SCORE;
00140     }
00141 
00142     map_location recruit_loc = find_vacant_tile(*resources::game_map, *resources::units, leader->get_location(), pathfind::VACANT_CASTLE);
00143     if (!resources::game_map->on_board(recruit_loc)) {
00144         return BAD_SCORE;
00145     }
00146 
00147     //note that no gold check is done. This is intended, to speed up recruitment_phase::evaluate()
00148     //so, after 1st failed recruit, this candidate action will be blacklisted for 1 turn.
00149 
00150     return get_score();
00151 }
00152 
00153 void aspect_recruitment_phase::execute()
00154 {
00155     raise_user_interact();
00156     stage_ptr r = get_recruitment(*this);
00157     if (r) {
00158         r->play_stage();
00159     } else {
00160         ERR_AI_TESTING_AI_DEFAULT << "no recruitment aspect - skipping recruitment" << std::endl;
00161     }
00162 }
00163 
00164 //==============================================================
00165 
00166 recruitment_phase::recruitment_phase( rca_context &context, const config &cfg )
00167     : candidate_action(context,cfg)
00168     , unit_movement_scores_()
00169     , not_recommended_units_()
00170     , unit_combat_scores_()
00171 {
00172 }
00173 
00174 
00175 recruitment_phase::~recruitment_phase()
00176 {
00177 }
00178 
00179 double recruitment_phase::evaluate()
00180 {
00181     const unit_map::const_iterator leader = resources::units->find_leader(get_side());
00182     if(leader == resources::units->end()) {
00183         return BAD_SCORE;
00184     }
00185     if (!resources::game_map->is_keep(leader->get_location())) {
00186         return BAD_SCORE;
00187     }
00188 
00189     std::set<map_location> checked_hexes;
00190     checked_hexes.insert(leader->get_location());
00191     if (count_free_hexes_in_castle(leader->get_location(), checked_hexes)==0) {
00192         return BAD_SCORE;
00193     }
00194 
00195     //note that no gold check is done. This is intended, to speed up recruitment_phase::evaluate()
00196     //so, after 1st failed recruit, this candidate action will be blacklisted for 1 turn.
00197 
00198     return get_score();
00199 }
00200 
00201 void recruitment_phase::execute()
00202 {
00203     not_recommended_units_.clear();
00204     unit_combat_scores_.clear();
00205     unit_movement_scores_.clear();
00206 
00207     unit_map &units_ = *resources::units;
00208     gamemap &map_ = *resources::game_map;
00209     std::vector<team> &teams_ = *resources::teams;
00210 
00211     map_location start_pos = units_.find_leader(get_side())->get_location();
00212 
00213     raise_user_interact();
00214     //analyze_potential_recruit_movements();
00215     analyze_potential_recruit_combat();
00216 
00217     std::vector<std::string> options = get_recruitment_pattern();
00218     if (std::count(options.begin(), options.end(), "scout") > 0) {
00219         size_t neutral_villages = 0;
00220 
00221         // We recruit the initial allocation of scouts
00222         // based on how many neutral villages there are
00223         // that are closer to us than to other keeps.
00224         const std::vector<map_location>& villages = map_.villages();
00225         for(std::vector<map_location>::const_iterator v = villages.begin(); v != villages.end(); ++v) {
00226             const int owner = village_owner(*v,teams_);
00227             if(owner == -1) {
00228                 const size_t distance = distance_between(start_pos,*v);
00229 
00230                 bool closest = true;
00231                 for(std::vector<team>::const_iterator i = teams_.begin(); i != teams_.end(); ++i) {
00232                     const int index = i - teams_.begin() + 1;
00233                     const map_location& loc = map_.starting_position(index);
00234                     if(loc != start_pos && distance_between(loc,*v) < distance) {
00235                         closest = false;
00236                         break;
00237                     }
00238                 }
00239 
00240                 if(closest) {
00241                     ++neutral_villages;
00242                 }
00243             }
00244         }
00245 
00246         // The villages per scout is for a two-side battle,
00247         // accounting for all neutral villages on the map.
00248         // We only look at villages closer to us, so we halve it,
00249         // making us get twice as many scouts.
00250         const int villages_per_scout = get_villages_per_scout()/2;
00251 
00252         // Get scouts depending on how many neutral villages there are.
00253         int scouts_wanted = villages_per_scout > 0 ? neutral_villages/villages_per_scout : 0;
00254 
00255         LOG_AI_TESTING_AI_DEFAULT << "scouts_wanted: " << neutral_villages << "/"
00256             << villages_per_scout << " = " << scouts_wanted << "\n";
00257 
00258         std::map<std::string,int> unit_types;
00259 
00260         for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) {
00261             if (u->side() == get_side()) {
00262                 ++unit_types[u->usage()];
00263             }
00264         }
00265 
00266         LOG_AI_TESTING_AI_DEFAULT << "we have " << unit_types["scout"] << " scouts already and we want "
00267             << scouts_wanted << " in total\n";
00268 
00269         while(unit_types["scout"] < scouts_wanted) {
00270             if (!recruit_usage("scout")){
00271                 break;
00272             }
00273             ++unit_types["scout"];
00274         }
00275     }
00276 
00277     // If there is no recruitment_pattern use "" which makes us consider
00278     // any unit available.
00279     if (options.empty()) {
00280         options.push_back("");
00281     }
00282     // Buy units as long as we have room and can afford it.
00283     while (recruit_usage(options[rand()%options.size()])) {
00284         //refresh the recruitment pattern - it can be changed by recruit_usage
00285         options = get_recruitment_pattern();
00286         if (options.empty()) {
00287             options.push_back("");
00288         }
00289     }
00290 
00291 }
00292 
00293 bool recruitment_phase::recruit_usage(const std::string& usage)
00294 {
00295     raise_user_interact();
00296 
00297     const int min_gold = 0;
00298 
00299     log_scope2(log_ai_testing_ai_default, "recruiting troops");
00300     LOG_AI_TESTING_AI_DEFAULT << "recruiting '" << usage << "'\n";
00301 
00302     //make sure id, usage and cost are known for the coming evaluation of unit types
00303     unit_types.build_all(unit_type::HELP_INDEX);
00304 
00305     std::vector<std::string> options;
00306     bool found = false;
00307     // Find an available unit that can be recruited,
00308     // matches the desired usage type, and comes in under budget.
00309     foreach (const std::string &name, current_team().recruits())
00310     {
00311         const unit_type *ut = unit_types.find(name);
00312         if (!ut) continue;
00313         // If usage is empty consider any unit.
00314         if (usage.empty() || ut->usage() == usage) {
00315             LOG_AI_TESTING_AI_DEFAULT << name << " considered for " << usage << " recruitment\n";
00316             found = true;
00317 
00318             if (current_team().gold() - ut->cost() < min_gold) {
00319                 LOG_AI_TESTING_AI_DEFAULT << name << " rejected, cost too high (cost: " << ut->cost() << ", current gold: " << current_team().gold() <<", min_gold: " << min_gold << ")\n";
00320                 continue;
00321             }
00322 
00323             if (not_recommended_units_.count(name))
00324             {
00325                 LOG_AI_TESTING_AI_DEFAULT << name << " rejected, bad terrain or combat\n";
00326                 continue;
00327             }
00328 
00329             LOG_AI_TESTING_AI_DEFAULT << "recommending '" << name << "'\n";
00330             options.push_back(name);
00331         }
00332     }
00333 
00334     // From the available options, choose one at random
00335     if(options.empty() == false) {
00336         const int option = rand()%options.size();
00337         recruit_result_ptr recruit_result = execute_recruit_action(options[option]);
00338         return recruit_result->is_ok();
00339     }
00340     if (found) {
00341         LOG_AI_TESTING_AI_DEFAULT << "No available units to recruit that come under the price.\n";
00342     } else if (usage != "") {
00343         //FIXME: This message should be suppressed when WML author
00344         //chooses the default recruitment pattern.
00345         const std::string warning = "At difficulty level " +
00346             resources::state_of_game->classification().difficulty + ", trying to recruit a:" +
00347             usage + " but no unit of that type (usage=) is"
00348             " available. Check the recruit and [ai]"
00349             " recruitment_pattern keys for team '" +
00350             current_team().name() + "' (" +
00351             lexical_cast<std::string>(get_side()) + ")"
00352             " against the usage key of the"
00353             " units in question! Removing invalid"
00354             " recruitment_pattern entry and continuing...\n";
00355         WRN_AI_TESTING_AI_DEFAULT << warning;
00356         // Uncommented until the recruitment limiting macro can be fixed to not trigger this warning.
00357         //lg::wml_error << warning;
00358         //@fixme
00359         //return current_team_w().remove_recruitment_pattern_entry(usage);
00360         return false;
00361     }
00362     return false;
00363 }
00364 
00365 int recruitment_phase::average_resistance_against(const unit_type& a, const unit_type& b) const
00366 {
00367     gamemap &map_ = *resources::game_map;
00368 
00369     int weighting_sum = 0, defense = 0;
00370     const std::map<t_translation::t_terrain, size_t>& terrain =
00371         map_.get_weighted_terrain_frequencies();
00372 
00373     for (std::map<t_translation::t_terrain, size_t>::const_iterator j = terrain.begin(),
00374          j_end = terrain.end(); j != j_end; ++j)
00375     {
00376         // Use only reachable tiles when computing the average defense.
00377       if (a.movement_type().movement_cost(map_, j->first) < unit_movement_type::UNREACHABLE) {
00378             defense += a.movement_type().defense_modifier(map_, j->first) * j->second;
00379             weighting_sum += j->second;
00380         }
00381     }
00382 
00383     if (weighting_sum == 0) {
00384         // This unit can't move on this map, so just get the average weighted
00385         // of all available terrains. This still is a kind of silly
00386         // since the opponent probably can't recruit this unit and it's a static unit.
00387         for (std::map<t_translation::t_terrain, size_t>::const_iterator jj = terrain.begin(),
00388                 jj_end = terrain.end(); jj != jj_end; ++jj)
00389         {
00390             defense += a.movement_type().defense_modifier(map_, jj->first) * jj->second;
00391             weighting_sum += jj->second;
00392         }
00393     }
00394 
00395     if(weighting_sum != 0) {
00396         defense /= weighting_sum;
00397     } else {
00398         ERR_AI_TESTING_AI_DEFAULT << "The weighting sum is 0 and is ignored.\n";
00399     }
00400 
00401     LOG_AI_TESTING_AI_DEFAULT << "average defense of '" << a.id() << "': " << defense << "\n";
00402 
00403     int sum = 0, weight_sum = 0;
00404 
00405     // calculation of the average damage taken
00406     bool steadfast = a.has_ability_by_id("steadfast");
00407     bool living = !a.not_living();
00408     const std::vector<attack_type>& attacks = b.attacks();
00409     for (std::vector<attack_type>::const_iterator i = attacks.begin(),
00410          i_end = attacks.end(); i != i_end; ++i)
00411     {
00412         int resistance = a.movement_type().resistance_against(*i);
00413         // Apply steadfast resistance modifier.
00414         if (steadfast && resistance < 100)
00415             resistance = std::max<int>(resistance * 2 - 100, 50);
00416         // Do not look for filters or values, simply assume 70% if CTH is customized.
00417         int cth = i->get_special_bool("chance_to_hit", true) ? 70 : defense;
00418         int weight = i->damage() * i->num_attacks();
00419         // if cth == 0 the division will do 0/0 so don't execute this part
00420         if (living && cth != 0 && i->get_special_bool("poison", true)) {
00421             // Compute the probability of not poisoning the unit.
00422             int prob = 100;
00423             for (int j = 0; j < i->num_attacks(); ++j)
00424                 prob = prob * (100 - cth);
00425             // Assume poison works one turn.
00426             weight += game_config::poison_amount * (100 - prob) / 100;
00427         }
00428         sum += cth * resistance * weight * weight; // average damage * weight
00429         weight_sum += weight;
00430     }
00431 
00432     // normalize by HP
00433     sum /= std::max<int>(1,std::min<int>(a.hitpoints(),1000)); // avoid values really out of range
00434 
00435     // Catch division by zero here if the attacking unit
00436     // has zero attacks and/or zero damage.
00437     // If it has no attack at all, the ai shouldn't prefer
00438     // that unit anyway.
00439     if (weight_sum == 0) {
00440         return sum;
00441     }
00442     return sum/weight_sum;
00443 }
00444 
00445 int recruitment_phase::compare_unit_types(const unit_type& a, const unit_type& b) const
00446 {
00447     const int a_effectiveness_vs_b = average_resistance_against(b,a);
00448     const int b_effectiveness_vs_a = average_resistance_against(a,b);
00449 
00450     LOG_AI_TESTING_AI_DEFAULT << "comparison of '" << a.id() << " vs " << b.id() << ": "
00451         << a_effectiveness_vs_b << " - " << b_effectiveness_vs_a << " = "
00452         << (a_effectiveness_vs_b - b_effectiveness_vs_a) << '\n';
00453     return a_effectiveness_vs_b - b_effectiveness_vs_a;
00454 }
00455 
00456 void recruitment_phase::analyze_potential_recruit_combat()
00457 {
00458     unit_map &units_ = *resources::units;
00459     if(unit_combat_scores_.empty() == false || get_recruitment_ignore_bad_combat()) {
00460         return;
00461     }
00462 
00463     log_scope2(log_ai_testing_ai_default, "analyze_potential_recruit_combat()");
00464 
00465     // Records the best combat analysis for each usage type.
00466     std::map<std::string,int> best_usage;
00467 
00468     const std::set<std::string>& recruits = current_team().recruits();
00469     std::set<std::string>::const_iterator i;
00470     for(i = recruits.begin(); i != recruits.end(); ++i) {
00471         const unit_type *info = unit_types.find(*i);
00472         if (!info || not_recommended_units_.count(*i)) {
00473             continue;
00474         }
00475 
00476         int score = 0, weighting = 0;
00477 
00478         for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
00479             if (j->can_recruit() || !current_team().is_enemy(j->side())) {
00480                 continue;
00481             }
00482 
00483             unit const &un = *j;
00484             const unit_type *enemy_info = unit_types.find(un.type_id());
00485             VALIDATE(enemy_info, "Unknown unit type : " + un.type_id() + " while scoring units.");
00486 
00487             int weight = un.cost() * un.hitpoints() / un.max_hitpoints();
00488             weighting += weight;
00489             score += compare_unit_types(*info, *enemy_info) * weight;
00490         }
00491 
00492         if(weighting != 0) {
00493             score /= weighting;
00494         }
00495 
00496         LOG_AI_TESTING_AI_DEFAULT << "combat score of '" << *i << "': " << score << "\n";
00497         unit_combat_scores_[*i] = score;
00498 
00499         if (best_usage.count(info->usage()) == 0 ||
00500                 score > best_usage[info->usage()]) {
00501             best_usage[info->usage()] = score;
00502         }
00503     }
00504 
00505     // Recommend not to use units of a certain usage type
00506     // if they have a score more than 600 below
00507     // the best unit of that usage type.
00508     for(i = recruits.begin(); i != recruits.end(); ++i) {
00509         const unit_type *info = unit_types.find(*i);
00510         if (!info || not_recommended_units_.count(*i)) {
00511             continue;
00512         }
00513 
00514         if (unit_combat_scores_[*i] + 600 < best_usage[info->usage()]) {
00515             LOG_AI_TESTING_AI_DEFAULT << "recommending not to use '" << *i << "' because of poor combat performance "
00516                       << unit_combat_scores_[*i] << "/" << best_usage[info->usage()] << "\n";
00517             not_recommended_units_.insert(*i);
00518         }
00519     }
00520 }
00521 
00522 
00523 //==============================================================
00524 
00525 combat_phase::combat_phase( rca_context &context, const config &cfg )
00526     : candidate_action(context,cfg),best_analysis_(),choice_rating_(-1000.0)
00527 {
00528 }
00529 
00530 combat_phase::~combat_phase()
00531 {
00532 }
00533 
00534 double combat_phase::evaluate()
00535 {
00536     choice_rating_ = -1000.0;
00537     int ticks = SDL_GetTicks();
00538 
00539     const std::vector<attack_analysis> analysis = get_attacks(); //passive_leader: in aspect_attacks::analyze_targets()
00540 
00541     int time_taken = SDL_GetTicks() - ticks;
00542     LOG_AI_TESTING_AI_DEFAULT << "took " << time_taken << " ticks for " << analysis.size()
00543         << " positions. Analyzing...\n";
00544 
00545     ticks = SDL_GetTicks();
00546 
00547     const int max_sims = 50000;
00548     int num_sims = analysis.empty() ? 0 : max_sims/analysis.size();
00549     if(num_sims < 20)
00550         num_sims = 20;
00551     if(num_sims > 40)
00552         num_sims = 40;
00553 
00554     LOG_AI_TESTING_AI_DEFAULT << "simulations: " << num_sims << "\n";
00555 
00556     const int max_positions = 30000;
00557     const int skip_num = analysis.size()/max_positions;
00558 
00559     std::vector<attack_analysis>::const_iterator choice_it = analysis.end();
00560     for(std::vector<attack_analysis>::const_iterator it = analysis.begin();
00561             it != analysis.end(); ++it) {
00562 
00563         if(skip_num > 0 && ((it - analysis.begin())%skip_num) && it->movements.size() > 1)
00564             continue;
00565 
00566         const double rating = it->rating(get_aggression(),*this);
00567         LOG_AI_TESTING_AI_DEFAULT << "attack option rated at " << rating << " ("
00568                       << (it->uses_leader ? get_leader_aggression() : get_aggression()) << ")\n";
00569 
00570         if(rating > choice_rating_) {
00571             choice_it = it;
00572             choice_rating_ = rating;
00573         }
00574     }
00575 
00576     time_taken = SDL_GetTicks() - ticks;
00577     LOG_AI_TESTING_AI_DEFAULT << "analysis took " << time_taken << " ticks\n";
00578 
00579 
00580     // suokko tested the rating against current_team().caution()
00581     // Bad mistake -- the AI became extremely reluctant to attack anything.
00582     // Documenting this in case someone has this bright idea again...*don't*...
00583     if(choice_rating_ > 0.0) {
00584         best_analysis_ = *choice_it;
00585         return get_score();
00586     } else {
00587         return BAD_SCORE;
00588     }
00589 }
00590 
00591 void combat_phase::execute()
00592 {
00593     assert(choice_rating_ > 0.0);
00594     map_location from   = best_analysis_.movements[0].first;
00595     map_location to     = best_analysis_.movements[0].second;
00596     map_location target_loc = best_analysis_.target;
00597 
00598     if (from!=to) {
00599         move_result_ptr move_res = execute_move_action(from,to,false);
00600         if (!move_res->is_ok()) {
00601             LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok, move failed" << std::endl;
00602             return;
00603         }
00604     }
00605 
00606     attack_result_ptr attack_res = check_attack_action(to, target_loc, -1);
00607     if (!attack_res->is_ok()) {
00608         LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok, attack cancelled" << std::endl;
00609     } else {
00610         attack_res->execute();
00611         if (!attack_res->is_ok()) {
00612             LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok, attack failed" << std::endl;
00613         }
00614     }
00615 
00616 }
00617 
00618 //==============================================================
00619 
00620 move_leader_to_goals_phase::move_leader_to_goals_phase( rca_context &context, const config &cfg )
00621     : candidate_action(context,cfg), auto_remove_(), dst_(), id_(), move_()
00622 {
00623 }
00624 
00625 move_leader_to_goals_phase::~move_leader_to_goals_phase()
00626 {
00627 }
00628 
00629 double move_leader_to_goals_phase::evaluate()
00630 {
00631 
00632     const config &goal = get_leader_goal();
00633     //passive leader can reach a goal
00634     if (!goal) {
00635         LOG_AI_TESTING_AI_DEFAULT << get_name() << "No goal found\n";
00636         return BAD_SCORE;
00637     }
00638 
00639     if (goal.empty()) {
00640         LOG_AI_TESTING_AI_DEFAULT << get_name() << "Empty goal found\n";
00641         return BAD_SCORE;
00642     }
00643 
00644     double max_risk = goal["max_risk"].to_double(1 - get_caution());
00645     auto_remove_ = goal["auto_remove"].to_bool();
00646 
00647     dst_ = map_location(goal, resources::state_of_game);
00648     if (!dst_.valid()) {
00649         ERR_AI_TESTING_AI_DEFAULT << "Invalid goal: "<<std::endl<<goal;
00650         return BAD_SCORE;
00651     }
00652 
00653     const unit_map::iterator leader = resources::units->find_leader(get_side());
00654     if (!leader.valid() || leader->incapacitated()) {
00655         WRN_AI_TESTING_AI_DEFAULT << "Leader not found\n";
00656         return BAD_SCORE;
00657     }
00658 
00659     id_ = goal["id"].str();
00660     if (leader->get_location() == dst_) {
00661         //goal already reached
00662         if (auto_remove_ && !id_.empty()) {
00663             remove_goal(id_);
00664         } else {
00665             move_ = check_move_action(leader->get_location(), leader->get_location(), !auto_remove_);//we do full moves if we don't want to remove goal
00666             if (move_->is_ok()) {
00667                 return get_score();
00668             } else {
00669                 return BAD_SCORE;
00670             }
00671         }
00672     }
00673 
00674     pathfind::shortest_path_calculator calc(*leader, current_team(), *resources::units, *resources::teams, *resources::game_map);
00675     pathfind::plain_route route = a_star_search(leader->get_location(), dst_, 1000.0, &calc,
00676             resources::game_map->w(), resources::game_map->h());
00677     if(route.steps.empty()) {
00678         LOG_AI_TESTING_AI_DEFAULT << "route empty";
00679         return BAD_SCORE;
00680     }
00681 
00682     const pathfind::paths leader_paths(*resources::game_map, *resources::units, *leader,
00683                  *resources::teams, false, true, current_team());
00684 
00685     std::map<map_location,pathfind::paths> possible_moves;
00686     possible_moves.insert(std::pair<map_location,pathfind::paths>(leader->get_location(), leader_paths));
00687 
00688     map_location loc;
00689     foreach (const map_location &l, route.steps)
00690     {
00691         if (leader_paths.destinations.contains(l) &&
00692             power_projection(l, get_enemy_dstsrc()) < leader->hitpoints() * max_risk)
00693         {
00694             loc = l;
00695         }
00696     }
00697 
00698     if(loc.valid()) {
00699         move_ = check_move_action(leader->get_location(), loc, false);
00700         if (move_->is_ok()) {
00701             return get_score();
00702         }
00703     }
00704     return BAD_SCORE;
00705 
00706 }
00707 
00708 void move_leader_to_goals_phase::execute()
00709 {
00710     move_->execute();
00711     if (!move_->is_ok()){
00712         LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
00713     }
00714     if (move_->get_unit_location()==dst_) {
00715         //goal already reached
00716         if (auto_remove_ && !id_.empty()) {
00717             remove_goal(id_);
00718         }
00719     }
00720 }
00721 
00722 void move_leader_to_goals_phase::remove_goal(const std::string &id)
00723 {
00724     config mod_ai;
00725     mod_ai["side"] = get_side();
00726     mod_ai["path"] = "aspect[leader_goal].facet["+id+"]";
00727     mod_ai["action"] = "delete";
00728     manager::modify_active_ai_for_side(get_side(),mod_ai);
00729 }
00730 
00731 //==============================================================
00732 
00733 move_leader_to_keep_phase::move_leader_to_keep_phase( rca_context &context, const config &cfg )
00734     : candidate_action(context,cfg),move_()
00735 {
00736 
00737 }
00738 
00739 move_leader_to_keep_phase::~move_leader_to_keep_phase()
00740 {
00741 
00742 }
00743 
00744 double move_leader_to_keep_phase::evaluate()
00745 {
00746     unit_map &units_ = *resources::units;
00747     const unit_map::iterator leader = units_.find_leader(get_side());
00748     if(get_passive_leader() && !get_passive_leader_shares_keep()){
00749         return BAD_SCORE;
00750     }
00751     if (leader == units_.end() || leader->incapacitated() || leader->movement_left() == 0) {
00752         return BAD_SCORE;
00753     }
00754 
00755     // Find where the leader can move
00756     const pathfind::paths leader_paths(*resources::game_map, units_, *leader,
00757         *resources::teams, false, true, current_team());
00758     const map_location& keep = suitable_keep(leader->get_location(), leader_paths);
00759     if (keep == map_location::null_location) {
00760         return BAD_SCORE;
00761     }
00762 
00763     std::map<map_location,pathfind::paths> possible_moves;
00764     possible_moves.insert(std::make_pair(leader->get_location(), leader_paths));
00765 
00766     // If the leader is not on keep, move him there.
00767     if (leader->get_location() != keep) {
00768         if (leader_paths.destinations.contains(keep) && units_.count(keep) == 0) {
00769             move_ = check_move_action(leader->get_location(), keep, false);
00770             if (move_->is_ok()){
00771                 return get_score();
00772             }
00773 
00774         }
00775         // Make a map of the possible locations the leader can move to,
00776         // ordered by the distance from the keep.
00777         std::multimap<int,map_location> moves_toward_keep;
00778 
00779         // The leader can't move to his keep, try to move to the closest location
00780         // to the keep where there are no enemies in range.
00781         int current_distance = distance_between(leader->get_location(), keep);
00782         foreach (const pathfind::paths::step &dest, leader_paths.destinations)
00783         {
00784             if (!units_.find(dest.curr).valid()){
00785                 const int new_distance = distance_between(dest.curr,keep);
00786                 if(new_distance < current_distance) {
00787                     moves_toward_keep.insert(std::make_pair(new_distance, dest.curr));
00788                 }
00789             }
00790         }
00791 
00792         // Find the first location which we can move to,
00793         // without the threat of enemies.
00794         for(std::multimap<int,map_location>::const_iterator j = moves_toward_keep.begin();
00795             j != moves_toward_keep.end(); ++j) {
00796 
00797             if(get_enemy_dstsrc().count(j->second) == 0) {
00798                 move_ = check_move_action(leader->get_location(), j->second, true);
00799                 if (move_->is_ok()){
00800                     return get_score();
00801                 }
00802             }
00803         }
00804     }
00805     return BAD_SCORE;
00806 }
00807 
00808 void move_leader_to_keep_phase::execute()
00809 {
00810     move_->execute();
00811     if (!move_->is_ok()){
00812         LOG_AI_TESTING_AI_DEFAULT <<  get_name() <<"::execute not ok" << std::endl;
00813     }
00814 }
00815 
00816 //==============================================================
00817 
00818 get_villages_phase::get_villages_phase( rca_context &context, const config &cfg )
00819     : candidate_action(context,cfg)
00820     , keep_loc_()
00821     , leader_loc_()
00822     , best_leader_loc_()
00823     , debug_(false)
00824     , moves_()
00825 {
00826 }
00827 
00828 get_villages_phase::~get_villages_phase()
00829 {
00830 }
00831 
00832 double get_villages_phase::evaluate()
00833 {
00834     moves_.clear();
00835     unit_map::const_iterator leader = resources::units->find_leader(get_side());
00836     get_villages(get_dstsrc(),get_enemy_dstsrc(),leader);
00837     if (!moves_.empty()) {
00838         return get_score();
00839     }
00840     return BAD_SCORE;
00841 }
00842 
00843 
00844 void get_villages_phase::execute()
00845 {
00846     unit_map &units_ = *resources::units;
00847     unit_map::const_iterator leader = units_.find_leader(get_side());
00848     // Move all the units to get villages, however move the leader last,
00849     // so that the castle will be cleared if it wants to stop to recruit along the way.
00850     std::pair<map_location,map_location> leader_move;
00851 
00852     for(tmoves::const_iterator i = moves_.begin(); i != moves_.end(); ++i) {
00853 
00854         if(leader != units_.end() && leader->get_location() == i->second) {
00855             leader_move = *i;
00856         } else {
00857             if (find_visible_unit(i->first, current_team()) == units_.end()) {
00858                 move_result_ptr move_res = execute_move_action(i->second,i->first,true);
00859                 if (!move_res->is_ok()) {
00860                     return;
00861                 }
00862 
00863                 const map_location loc = move_res->get_unit_location();
00864                 leader = units_.find_leader(get_side());
00865                 const unit_map::const_iterator new_unit = units_.find(loc);
00866 
00867                 if (new_unit != units_.end() &&
00868                     power_projection(i->first, get_enemy_dstsrc()) >= new_unit->hitpoints() / 4)
00869                 {
00870                     LOG_AI_TESTING_AI_DEFAULT << "found support target... " << new_unit->get_location() << '\n';
00871                     //FIXME: suokko tweaked the constant 1.0 to the formula:
00872                     //25.0* current_team().caution() * power_projection(loc,enemy_dstsrc) / new_unit->second.hitpoints()
00873                     //Is this an improvement?
00874 
00875                     ///@todo 1.7 check if this an improvement
00876                     //add_target(target(new_unit->first,1.0,target::SUPPORT));
00877                 }
00878             }
00879         }
00880     }
00881 
00882     if(leader_move.second.valid()) {
00883         if((find_visible_unit(leader_move.first , current_team()) == units_.end())
00884            && resources::game_map->is_village(leader_move.first)) {
00885             move_result_ptr move_res = execute_move_action(leader_move.second,leader_move.first,true);
00886             if (!move_res->is_ok()) {
00887                 return;
00888             }
00889         }
00890     }
00891 
00892     return;
00893 }
00894 
00895 void get_villages_phase::get_villages(
00896         const move_map& dstsrc, const move_map& enemy_dstsrc,
00897         unit_map::const_iterator &leader)
00898 {
00899     DBG_AI_TESTING_AI_DEFAULT << "deciding which villages we want...\n";
00900     unit_map &units_ = *resources::units;
00901     const int ticks = SDL_GetTicks();
00902     best_leader_loc_ = map_location::null_location;
00903     if(leader != units_.end()) {
00904         keep_loc_ = nearest_keep(leader->get_location());
00905         leader_loc_ = leader->get_location();
00906     } else {
00907         keep_loc_ = map_location::null_location;
00908         leader_loc_ = map_location::null_location;
00909     }
00910 
00911     debug_ = !lg::debug.dont_log(log_ai_testing_ai_default);
00912 
00913     // Find our units who can move.
00914     treachmap reachmap;
00915     for(unit_map::const_iterator u_itor = units_.begin();
00916             u_itor != units_.end(); ++u_itor) {
00917         if(u_itor->can_recruit() && get_passive_leader()){
00918             continue;
00919         }
00920         if(u_itor->side() == get_side() && u_itor->movement_left()) {
00921             reachmap.insert(std::make_pair(u_itor->get_location(),  std::vector<map_location>()));
00922         }
00923     }
00924 
00925 
00926     DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units found who can try to capture a village.\n";
00927 
00928     find_villages(reachmap, moves_, dstsrc, enemy_dstsrc);
00929 
00930     treachmap::iterator itor = reachmap.begin();
00931     while(itor != reachmap.end()) {
00932         if(itor->second.size() == 0) {
00933             itor = remove_unit(reachmap, moves_, itor);
00934         } else {
00935             ++itor;
00936         }
00937     }
00938 
00939     if(!reachmap.empty()) {
00940         DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units left after removing the ones who "
00941             "can't reach a village, send the to the dispatcher.\n";
00942 
00943         dump_reachmap(reachmap);
00944 
00945         dispatch(reachmap, moves_);
00946     } else {
00947         DBG_AI_TESTING_AI_DEFAULT << "No more units left after removing the ones who can't reach a village.\n";
00948     }
00949 
00950     LOG_AI_TESTING_AI_DEFAULT << "Village assignment done: " << (SDL_GetTicks() - ticks)
00951         << " ms, resulted in " << moves_.size() << " units being dispatched.\n";
00952 
00953 }
00954 
00955 void get_villages_phase::find_villages(
00956     treachmap& reachmap,
00957     tmoves& moves,
00958     const std::multimap<map_location,map_location>& dstsrc,
00959     const std::multimap<map_location,map_location>& enemy_dstsrc)
00960 
00961 {
00962     std::map<map_location, double> vulnerability;
00963 
00964     const bool passive_leader = get_passive_leader();
00965 
00966     size_t min_distance = 100000;
00967     gamemap &map_ = *resources::game_map;
00968     std::vector<team> &teams_ = *resources::teams;
00969 
00970     // When a unit is dispatched we need to make sure we don't
00971     // dispatch this unit a second time, so store them here.
00972     std::vector<map_location> dispatched_units;
00973     for(std::multimap<map_location, map_location>::const_iterator
00974             j = dstsrc.begin();
00975             j != dstsrc.end(); ++j) {
00976 
00977         const map_location &current_loc = j->first;
00978 
00979         if(j->second == leader_loc_) {
00980             if(passive_leader) {
00981                 continue;
00982             }
00983 
00984             const size_t distance = distance_between(keep_loc_, current_loc);
00985             if(distance < min_distance) {
00986                 min_distance = distance;
00987                 best_leader_loc_ = current_loc;
00988             }
00989         }
00990 
00991         if(std::find(dispatched_units.begin(), dispatched_units.end(),
00992                 j->second) != dispatched_units.end()) {
00993             continue;
00994         }
00995 
00996         if(map_.is_village(current_loc) == false) {
00997             continue;
00998         }
00999 
01000         bool want_village = true, owned = false;
01001         for(size_t n = 0; n != teams_.size(); ++n) {
01002             owned = teams_[n].owns_village(current_loc);
01003             if(owned && !current_team().is_enemy(n+1)) {
01004                 want_village = false;
01005             }
01006 
01007             if(owned) {
01008                 break;
01009             }
01010         }
01011 
01012         if(want_village == false) {
01013             continue;
01014         }
01015 
01016         // If it is a neutral village, and we have no leader,
01017         // then the village is of no use to us, and we don't want it.
01018         if(!owned && leader_loc_ == map_location::null_location) {
01019             continue;
01020         }
01021 
01022         double threat = 0.0;
01023         const std::map<map_location,double>::const_iterator vuln = vulnerability.find(current_loc);
01024         if(vuln != vulnerability.end()) {
01025             threat = vuln->second;
01026         } else {
01027             threat = power_projection(current_loc,enemy_dstsrc);
01028             vulnerability.insert(std::pair<map_location,double>(current_loc,threat));
01029         }
01030 
01031         const unit_map::const_iterator u = resources::units->find(j->second);
01032         if (u == resources::units->end() || u->get_state("guardian")) {
01033             continue;
01034         }
01035 
01036         const unit  &un = *u;
01037         //FIXME: suokko turned this 2:1 to 1.5:1.0.
01038         //and dropped the second term of the multiplication.  Is that better?
01039         //const double threat_multipler = (current_loc == leader_loc?2:1) * current_team().caution() * 10;
01040         if(un.hitpoints() < (threat*2*un.defense_modifier(map_.get_terrain(current_loc)))/100) {
01041             continue;
01042         }
01043 
01044         // If the next and previous destination differs from our current destination,
01045         // we're the only one who can reach the village -> dispatch.
01046         std::multimap<map_location, map_location>::const_iterator next = j;
01047         ++next; // j + 1 fails
01048         const bool at_begin = (j == dstsrc.begin());
01049         std::multimap<map_location, map_location>::const_iterator prev = j; //FIXME seems not to work
01050         if(!at_begin) {
01051             --prev;
01052         }
01053 #if 1
01054         if((next == dstsrc.end() || next->first != current_loc)
01055                 && (at_begin || prev->first != current_loc)) {
01056 
01057             move_result_ptr move_check_res = check_move_action(j->second,j->first,true);
01058             if (move_check_res->is_ok()) {
01059                 DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << j->second << " to village " << j->first << '\n';
01060                 moves.push_back(std::make_pair(j->first, j->second));
01061             }
01062             reachmap.erase(j->second);
01063             dispatched_units.push_back(j->second);
01064             continue;
01065         }
01066 #endif
01067         reachmap[j->second].push_back(current_loc);
01068     }
01069 
01070     DBG_AI_TESTING_AI_DEFAULT << moves.size() << " units already dispatched, "
01071         << reachmap.size() << " left to evaluate.\n";
01072 }
01073 
01074 void get_villages_phase::dispatch(treachmap& reachmap, tmoves& moves)
01075 {
01076     DBG_AI_TESTING_AI_DEFAULT << "Starting simple dispatch.\n";
01077 
01078     // we now have a list with units with the villages they can reach.
01079     // keep trying the following steps as long as one of them changes
01080     // the state.
01081     // 1. Dispatch units who can reach 1 village (if more units can reach that
01082     //    village only one can capture it, so use the first in the list.)
01083     // 2. Villages which can only be reached by one unit get that unit dispatched
01084     //    to them.
01085     size_t village_count = 0;
01086     bool dispatched = true;
01087     while(dispatched) {
01088         dispatched = false;
01089 
01090         if(dispatch_unit_simple(reachmap, moves)) {
01091             dispatched = true;
01092         } else {
01093             if(reachmap.empty()) {
01094                 DBG_AI_TESTING_AI_DEFAULT << "dispatch_unit_simple() found a final solution.\n";
01095                 break;
01096             } else {
01097                 DBG_AI_TESTING_AI_DEFAULT << "dispatch_unit_simple() couldn't dispatch more units.\n";
01098             }
01099         }
01100 
01101         if(dispatch_village_simple(reachmap, moves, village_count)) {
01102             dispatched = true;
01103         } else {
01104             if(reachmap.empty()) {
01105                 DBG_AI_TESTING_AI_DEFAULT << "dispatch_village_simple() found a final solution.\n";
01106                 break;
01107             } else {
01108                 DBG_AI_TESTING_AI_DEFAULT << "dispatch_village_simple() couldn't dispatch more units.\n";
01109             }
01110         }
01111 
01112         if(reachmap.size() != 0 && dispatched) {
01113             DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " unit(s) left restarting simple dispatching.\n";
01114 
01115             dump_reachmap(reachmap);
01116         }
01117     }
01118 
01119     if(reachmap.size() == 0) {
01120         DBG_AI_TESTING_AI_DEFAULT << "No units left after simple dispatcher.\n";
01121         return;
01122     }
01123 
01124     DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units left for complex dispatch with "
01125         << village_count << " villages left.\n";
01126 
01127     dump_reachmap(reachmap);
01128 
01129     dispatch_complex(reachmap, moves, village_count);
01130 }
01131 
01132 // Returns      need further processing
01133 // false        Nothing has been modified or no units left
01134 bool get_villages_phase::dispatch_unit_simple(treachmap& reachmap, tmoves& moves)
01135 {
01136     bool result = false;
01137 
01138     treachmap::iterator itor = reachmap.begin();
01139     while(itor != reachmap.end()) {
01140         if(itor->second.size() == 1) {
01141             const map_location village = itor->second[0];
01142             result = true;
01143 
01144             DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->first << " to village " << village << '\n';
01145             moves.push_back(std::make_pair(village, itor->first));
01146             reachmap.erase(itor++);
01147 
01148             if(remove_village(reachmap, moves, village)) {
01149                 itor = reachmap.begin();
01150             }
01151 
01152         } else {
01153             ++itor;
01154         }
01155     }
01156 
01157     // Test special cases.
01158     if(reachmap.empty()) {
01159         // We're done.
01160         return false;
01161     }
01162 
01163     if(reachmap.size() == 1) {
01164         // One unit left.
01165         DBG_AI_TESTING_AI_DEFAULT << "Dispatched _last_ unit at " << reachmap.begin()->first
01166             << " to village " << reachmap.begin()->second[0] << '\n';
01167 
01168         moves.push_back(std::make_pair(
01169             reachmap.begin()->second[0], reachmap.begin()->first));
01170 
01171         reachmap.clear();
01172         // We're done.
01173         return false;
01174     }
01175 
01176     return result;
01177 }
01178 
01179 bool get_villages_phase::dispatch_village_simple(
01180     treachmap& reachmap, tmoves& moves, size_t& village_count)
01181 {
01182 
01183     bool result = false;
01184     bool dispatched = true;
01185     while(dispatched) {
01186         dispatched = false;
01187 
01188         // build the reverse map
01189         std::map<map_location /*village location*/,
01190             std::vector<map_location /* units that can reach it*/> >reversemap;
01191 
01192         treachmap::const_iterator itor = reachmap.begin();
01193         for(;itor != reachmap.end(); ++itor) {
01194 
01195             for(std::vector<map_location>::const_iterator
01196                     v_itor = itor->second.begin();
01197                     v_itor != itor->second.end(); ++v_itor) {
01198 
01199                 reversemap[*v_itor].push_back(itor->first);
01200 
01201             }
01202         }
01203 
01204         village_count = reversemap.size();
01205 
01206         itor = reversemap.begin();
01207         while(itor != reversemap.end()) {
01208             if(itor->second.size() == 1) {
01209                 // One unit can reach this village.
01210                 const map_location village = itor->first;
01211                 dispatched = true;
01212                 result = true;
01213 
01214                 DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->second[0] << " to village " << itor->first << '\n';
01215                 moves.push_back(std::make_pair(itor->first, itor->second[0]));
01216 
01217                 reachmap.erase(itor->second[0]);
01218                 remove_village(reachmap, moves, village);
01219                 // Get can go to some trouble to remove the unit from the other villages
01220                 // instead we abort this loop end do a full rebuild on the map.
01221                 break;
01222             } else {
01223                 ++itor;
01224             }
01225         }
01226     }
01227 
01228     return result;
01229 }
01230 
01231 bool get_villages_phase::remove_village(
01232     treachmap& reachmap, tmoves& moves, const map_location& village)
01233 {
01234     bool result = false;
01235     treachmap::iterator itor = reachmap.begin();
01236     while(itor != reachmap.end()) {
01237         itor->second.erase(std::remove(itor->second.begin(), itor->second.end(), village), itor->second.end());
01238         if(itor->second.empty()) {
01239             result = true;
01240             itor = remove_unit(reachmap, moves, itor);
01241         } else {
01242             ++itor;
01243         }
01244     }
01245     return result;
01246 }
01247 
01248 get_villages_phase::treachmap::iterator get_villages_phase::remove_unit(
01249     treachmap& reachmap, tmoves& moves, treachmap::iterator unit)
01250 {
01251     assert(unit->second.empty());
01252 
01253     if(unit->first == leader_loc_ && best_leader_loc_ != map_location::null_location) {
01254         DBG_AI_TESTING_AI_DEFAULT << "Dispatch leader at " << leader_loc_ << " closer to the keep at "
01255             << best_leader_loc_ << '\n';
01256 
01257         moves.push_back(std::make_pair(best_leader_loc_, leader_loc_));
01258     }
01259 
01260     reachmap.erase(unit++);
01261     return unit;
01262 }
01263 
01264 void get_villages_phase::dispatch_complex(
01265     treachmap& reachmap, tmoves& moves, const size_t village_count)
01266 {
01267     // ***** ***** Init and dispatch if every unit can reach every village.
01268 
01269     const size_t unit_count = reachmap.size();
01270     // The maximum number of villages we can capture with the available units.
01271     const size_t max_result = unit_count < village_count ? unit_count : village_count;
01272 
01273     assert(unit_count >= 2 && village_count >= 2);
01274 
01275     // Every unit can reach every village.
01276     if(unit_count == 2 && village_count == 2) {
01277         DBG_AI_TESTING_AI_DEFAULT << "Every unit can reach every village for 2 units, dispatch them.\n";
01278         full_dispatch(reachmap, moves);
01279         return;
01280     }
01281 
01282     std::vector<map_location> units(unit_count);
01283     std::vector<size_t> villages_per_unit(unit_count);
01284     std::vector<map_location> villages;
01285     std::vector<size_t> units_per_village(village_count);
01286 
01287     // We want to test the units, the ones who can reach the least
01288     // villages first so this is our lookup map.
01289     std::multimap<size_t /* villages_per_unit value*/,
01290         size_t /*villages_per_unit index*/> unit_lookup;
01291 
01292     std::vector</*unit*/std::vector</*village*/bool> >
01293         matrix(reachmap.size(), std::vector<bool>(village_count, false));
01294 
01295     treachmap::const_iterator itor = reachmap.begin();
01296     for(size_t u = 0; u < unit_count; ++u, ++itor) {
01297         units[u] = itor->first;
01298         villages_per_unit[u] = itor->second.size();
01299         unit_lookup.insert(std::make_pair(villages_per_unit[u], u));
01300 
01301         assert(itor->second.size() >= 2);
01302 
01303         for(size_t v = 0; v < itor->second.size(); ++v) {
01304 
01305             size_t v_index;
01306             // find the index of the v in the villages
01307             std::vector<map_location>::const_iterator v_itor =
01308                 std::find(villages.begin(), villages.end(), itor->second[v]);
01309             if(v_itor == villages.end()) {
01310                 v_index = villages.size(); // will be the last element after push_back.
01311                 villages.push_back(itor->second[v]);
01312             } else {
01313                 v_index = v_itor - villages.begin();
01314             }
01315 
01316             units_per_village[v_index]++;
01317 
01318             matrix[u][v_index] = true;
01319         }
01320     }
01321     for(std::vector<size_t>::const_iterator upv_it = units_per_village.begin();
01322             upv_it != units_per_village.end(); ++upv_it) {
01323 
01324         assert(*upv_it >=2);
01325     }
01326 
01327     if(debug_) {
01328         // Print header
01329         std::cerr << "Reach matrix:\n\nvillage";
01330         size_t u, v;
01331         for(v = 0; v < village_count; ++v) {
01332             std::cerr << '\t' << villages[v];
01333         }
01334         std::cerr << "\ttotal\nunit\n";
01335 
01336         // Print data
01337         for(u = 0; u < unit_count; ++u) {
01338             std::cerr << units[u];
01339 
01340             for(size_t v = 0; v < village_count; ++v) {
01341                 std::cerr << '\t' << matrix[u][v];
01342             }
01343             std::cerr << "\t" << villages_per_unit[u] << '\n';
01344         }
01345 
01346         // Print footer
01347         std::cerr << "total";
01348         for(v = 0; v < village_count; ++v) {
01349             std::cerr << '\t' << units_per_village[v];
01350         }
01351         std::cerr << '\n';
01352     }
01353 
01354     // Test the special case, everybody can reach all villages
01355     const bool reach_all = ((village_count == unit_count)
01356         && (std::accumulate(villages_per_unit.begin(), villages_per_unit.end(), size_t())
01357         == (village_count * unit_count)));
01358 
01359     if(reach_all) {
01360         DBG_AI_TESTING_AI_DEFAULT << "Every unit can reach every village, dispatch them\n";
01361         full_dispatch(reachmap, moves);
01362         reachmap.clear();
01363         return;
01364     }
01365 
01366     // ***** ***** Find a square
01367     std::multimap<size_t /* villages_per_unit value*/, size_t /*villages_per_unit index*/>
01368         ::const_iterator src_itor =  unit_lookup.begin();
01369 
01370     while(src_itor != unit_lookup.end() && src_itor->first == 2) {
01371 
01372         for(std::multimap<size_t, size_t>::const_iterator
01373                 dst_itor = unit_lookup.begin();
01374                 dst_itor != unit_lookup.end(); ++ dst_itor) {
01375 
01376             // avoid comparing us with ourselves.
01377             if(src_itor == dst_itor) {
01378                 continue;
01379             }
01380 
01381             std::vector<bool> result;
01382             std::transform(matrix[src_itor->second].begin(), matrix[src_itor->second].end(),
01383                 matrix[dst_itor->second].begin(),
01384                 std::back_inserter(result),
01385                 std::logical_and<bool>()
01386                 );
01387 
01388             size_t matched = std::count(result.begin(), result.end(), true);
01389 
01390             // we found a  solution, dispatch
01391             if(matched == 2) {
01392                 // Collect data
01393                 std::vector<bool>::iterator first = std::find(result.begin(), result.end(), true);
01394                 std::vector<bool>::iterator second = std::find(first + 1, result.end(), true);
01395 
01396                 const map_location village1 = villages[first - result.begin()];
01397                 const map_location village2 = villages[second - result.begin()];
01398 
01399                 const bool perfect = (src_itor->first == 2 &&
01400                     dst_itor->first == 2 &&
01401                     units_per_village[first - result.begin()] == 2 &&
01402                     units_per_village[second - result.begin()] == 2);
01403 
01404                 // Dispatch
01405                 DBG_AI_TESTING_AI_DEFAULT << "Found a square.\nDispatched unit at " << units[src_itor->second]
01406                         << " to village " << village1 << '\n';
01407                 moves.push_back(std::make_pair(village1, units[src_itor->second]));
01408 
01409                 DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << units[dst_itor->second]
01410                         << " to village " << village2 << '\n';
01411                 moves.push_back(std::make_pair(village2, units[dst_itor->second]));
01412 
01413                 // Remove the units
01414                 reachmap.erase(units[src_itor->second]);
01415                 reachmap.erase(units[dst_itor->second]);
01416 
01417                 // Evaluate and start correct function.
01418                 if(perfect) {
01419                     // We did a perfect dispatch 2 units who could visit 2 villages.
01420                     // This means we didn't change the assertion for this functions
01421                     // so call ourselves recursively, and finish afterwards.
01422                     DBG_AI_TESTING_AI_DEFAULT << "Perfect dispatch, do complex again.\n";
01423                     dispatch_complex(reachmap, moves, village_count - 2);
01424                     return;
01425                 } else {
01426                     // We did a not perfect dispatch but we did modify things
01427                     // so restart dispatching.
01428                     DBG_AI_TESTING_AI_DEFAULT << "NON Perfect dispatch, do dispatch again.\n";
01429                     remove_village(reachmap, moves, village1);
01430                     remove_village(reachmap, moves, village2);
01431                     dispatch(reachmap, moves);
01432                     return;
01433                 }
01434             }
01435         }
01436 
01437         ++src_itor;
01438     }
01439 
01440     // ***** ***** Do all permutations.
01441     // Now walk through all possible permutations
01442     // - test whether the suggestion is possible
01443     // - does it result in max_villages
01444     //   - dispatch and ready
01445     // - is it's result better as the last best
01446     //   - store
01447     std::vector<std::pair<map_location, map_location> > best_result;
01448 
01449     // Bruteforcing all possible permutations can result in a slow game.
01450     // So there needs to be a balance between the best possible result and
01451     // not too slow. From the test (at the end of the file) a good number is
01452     // picked. In general we shouldn't reach this point too often if we do
01453     // there are a lot of villages which are unclaimed and a lot of units
01454     // to claim them.
01455     const size_t max_options = 8;
01456     if(unit_count >= max_options && village_count >= max_options) {
01457 
01458         DBG_AI_TESTING_AI_DEFAULT << "Too many units " << unit_count << " and villages "
01459             << village_count<<" found, evaluate only the first "
01460             << max_options << " options;\n";
01461 
01462         std::vector<size_t> perm (max_options, 0);
01463         for(size_t i =0; i < max_options; ++i) {
01464             perm[i] = i;
01465         }
01466         while(std::next_permutation(perm.begin(), perm.end())) {
01467 
01468             // Get result for current permutation.
01469             std::vector<std::pair<map_location,map_location> > result;
01470             for(size_t u = 0; u < max_options; ++u) {
01471                 if(matrix[u][perm[u]]) {
01472                     result.push_back(std::make_pair(villages[perm[u]], units[u]));
01473 
01474                 }
01475             }
01476             if(result.size() == max_result) {
01477                 best_result.swap(result);
01478                 break;
01479             }
01480 
01481             if(result.size() > best_result.size()) {
01482                 best_result.swap(result);
01483             }
01484         }
01485         // End of loop no optimal found, assign the best
01486         std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
01487 
01488         // Clean up the reachmap for dispatched units.
01489         for(std::vector<std::pair<map_location, map_location> >::const_iterator
01490                 itor = best_result.begin(); itor != best_result.end(); ++itor) {
01491             reachmap.erase(itor->second);
01492         }
01493 
01494         // Try to dispatch whatever is left
01495         dispatch(reachmap, moves);
01496         return;
01497 
01498     } else if(unit_count <= village_count) {
01499 
01500         DBG_AI_TESTING_AI_DEFAULT << "Unit major\n";
01501 
01502         std::vector<size_t> perm (unit_count, 0);
01503         for(size_t i =0; i < unit_count; ++i) {
01504             perm[i] = i;
01505         }
01506         while(std::next_permutation(perm.begin(), perm.end())) {
01507             // Get result for current permutation.
01508             std::vector<std::pair<map_location,map_location> > result;
01509             for(size_t u = 0; u < unit_count; ++u) {
01510                 if(matrix[u][perm[u]]) {
01511                     result.push_back(std::make_pair(villages[perm[u]], units[u]));
01512 
01513                 }
01514             }
01515             if(result.size() == max_result) {
01516                 std::copy(result.begin(), result.end(), std::back_inserter(moves));
01517                 reachmap.clear();
01518                 return;
01519             }
01520 
01521             if(result.size() > best_result.size()) {
01522                 best_result.swap(result);
01523             }
01524         }
01525         // End of loop no optimal found, assign the best
01526         std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
01527 
01528         // clean up the reachmap we need to test whether the leader is still there
01529         // and if so remove him manually to get him dispatched.
01530         for(std::vector<std::pair<map_location, map_location> >::const_iterator
01531                 itor = best_result.begin(); itor != best_result.end(); ++itor) {
01532             reachmap.erase(itor->second);
01533         }
01534         treachmap::iterator unit = reachmap.find(leader_loc_);
01535         if(unit != reachmap.end()) {
01536             unit->second.clear();
01537             remove_unit(reachmap, moves, unit);
01538         }
01539         reachmap.clear();
01540 
01541     } else {
01542 
01543         DBG_AI_TESTING_AI_DEFAULT << "Village major\n";
01544 
01545         std::vector<size_t> perm (village_count, 0);
01546         for(size_t i =0; i < village_count; ++i) {
01547             perm[i] = i;
01548         }
01549         while(std::next_permutation(perm.begin(), perm.end())) {
01550             // Get result for current permutation.
01551             std::vector<std::pair<map_location,map_location> > result;
01552             for(size_t v = 0; v < village_count; ++v) {
01553                 if(matrix[perm[v]][v]) {
01554                     result.push_back(std::make_pair(villages[v], units[perm[v]]));
01555 
01556                 }
01557             }
01558             if(result.size() == max_result) {
01559                 std::copy(result.begin(), result.end(), std::back_inserter(moves));
01560                 reachmap.clear();
01561                 return;
01562             }
01563 
01564             if(result.size() > best_result.size()) {
01565                 best_result.swap(result);
01566             }
01567         }
01568         // End of loop no optimal found, assigne the best
01569         std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
01570 
01571         // clean up the reachmap we need to test whether the leader is still there
01572         // and if so remove him manually to get him dispatched.
01573         for(std::vector<std::pair<map_location, map_location> >::const_iterator
01574                 itor = best_result.begin(); itor != best_result.end(); ++itor) {
01575             reachmap.erase(itor->second);
01576         }
01577         treachmap::iterator unit = reachmap.find(leader_loc_);
01578         if(unit != reachmap.end()) {
01579             unit->second.clear();
01580             remove_unit(reachmap, moves, unit);
01581         }
01582         reachmap.clear();
01583     }
01584 }
01585 
01586 void get_villages_phase::full_dispatch(treachmap& reachmap, tmoves& moves)
01587 {
01588     treachmap::const_iterator itor = reachmap.begin();
01589     for(size_t i = 0; i < reachmap.size(); ++i, ++itor) {
01590         DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->first
01591                 << " to village " << itor->second[i] << '\n';
01592         moves.push_back(std::make_pair(itor->second[i], itor->first));
01593     }
01594 }
01595 
01596 void get_villages_phase::dump_reachmap(treachmap& reachmap)
01597 {
01598     if(!debug_) {
01599         return;
01600     }
01601 
01602     for(treachmap::const_iterator itor =
01603             reachmap.begin(); itor != reachmap.end(); ++itor) {
01604 
01605         std::cerr << "Reachlist for unit at " << itor->first;
01606 
01607         if(itor->second.empty()) {
01608             std::cerr << "\tNone";
01609         }
01610 
01611         for(std::vector<map_location>::const_iterator
01612                 v_itor = itor->second.begin();
01613                 v_itor != itor->second.end(); ++v_itor) {
01614 
01615             std::cerr << '\t' << *v_itor;
01616         }
01617         std::cerr << '\n';
01618 
01619     }
01620 }
01621 
01622 //==============================================================
01623 
01624 get_healing_phase::get_healing_phase( rca_context &context, const config &cfg )
01625     : candidate_action(context,cfg),move_()
01626 {
01627 }
01628 
01629 get_healing_phase::~get_healing_phase()
01630 {
01631 }
01632 
01633 double get_healing_phase::evaluate()
01634 {
01635     // Find units in need of healing.
01636     unit_map &units_ = *resources::units;
01637     unit_map::iterator u_it = units_.begin();
01638     for(; u_it != units_.end(); ++u_it) {
01639         unit &u = *u_it;
01640 
01641         if(u.can_recruit() && get_passive_leader()){
01642             continue;
01643         }
01644 
01645         // If the unit is on our side, has lost as many or more than
01646         // 1/2 round worth of healing, and doesn't regenerate itself,
01647         // then try to find a vacant village for it to rest in.
01648         if(u.side() == get_side() &&
01649            (u.max_hitpoints() - u.hitpoints() >= game_config::poison_amount/2
01650            || u.get_state(unit::STATE_POISONED)) &&
01651             !u.get_ability_bool("regenerate"))
01652         {
01653             // Look for the village which is the least vulnerable to enemy attack.
01654             typedef std::multimap<map_location,map_location>::const_iterator Itor;
01655             std::pair<Itor,Itor> it = get_srcdst().equal_range(u_it->get_location());
01656             double best_vulnerability = 100000.0;
01657             // Make leader units more unlikely to move to vulnerable villages
01658             const double leader_penalty = (u.can_recruit()?2.0:1.0);
01659             Itor best_loc = it.second;
01660             while(it.first != it.second) {
01661                 const map_location& dst = it.first->second;
01662                 if (resources::game_map->gives_healing(dst) && (units_.find(dst) == units_.end() || dst == u_it->get_location())) {
01663                     const double vuln = power_projection(it.first->first, get_enemy_dstsrc());
01664                     DBG_AI_TESTING_AI_DEFAULT << "found village with vulnerability: " << vuln << "\n";
01665                     if(vuln < best_vulnerability) {
01666                         best_vulnerability = vuln;
01667                         best_loc = it.first;
01668                         DBG_AI_TESTING_AI_DEFAULT << "chose village " << dst << '\n';
01669                     }
01670                 }
01671 
01672                 ++it.first;
01673             }
01674 
01675             // If we have found an eligible village,
01676             // and we can move there without expecting to get whacked next turn:
01677             if(best_loc != it.second && best_vulnerability*leader_penalty < u.hitpoints()) {
01678                 move_ = check_move_action(best_loc->first,best_loc->second,true);
01679                 if (move_->is_ok()) {
01680                     return get_score();
01681                 }
01682             }
01683         }
01684     }
01685 
01686     return BAD_SCORE;
01687 }
01688 
01689 void get_healing_phase::execute()
01690 {
01691     LOG_AI_TESTING_AI_DEFAULT << "moving unit to village for healing...\n";
01692     move_->execute();
01693     if (!move_->is_ok()){
01694         LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
01695     }
01696 }
01697 
01698 //==============================================================
01699 
01700 retreat_phase::retreat_phase( rca_context &context, const config &cfg )
01701     : candidate_action(context,cfg), move_()
01702 {
01703 }
01704 
01705 retreat_phase::~retreat_phase()
01706 {
01707 }
01708 
01709 double retreat_phase::evaluate()
01710 {
01711 
01712 
01713     // Get versions of the move map that assume that all units are at full movement
01714     const unit_map& units_ = *resources::units;
01715 
01716     //unit_map::const_iterator leader = units_.find_leader(get_side());
01717     std::vector<unit_map::const_iterator> leaders = units_.find_leaders(get_side());
01718     std::map<map_location,pathfind::paths> dummy_possible_moves;
01719 
01720     move_map fullmove_srcdst;
01721     move_map fullmove_dstsrc;
01722     calculate_possible_moves(dummy_possible_moves, fullmove_srcdst, fullmove_dstsrc,
01723             false, true, &get_avoid());
01724 
01725     /*map_location leader_adj[6];
01726     if(leader != units_.end()) {
01727         get_adjacent_tiles(leader->get_location(), leader_adj);
01728     }*/
01729     //int leader_adj_count = 0;
01730     std::vector<map_location> leaders_adj_v;
01731     foreach(unit_map::const_iterator leader, leaders){
01732         map_location tmp_leader_adj[6];
01733         get_adjacent_tiles(leader->get_location(), tmp_leader_adj);
01734         foreach(map_location &loc, tmp_leader_adj){
01735             bool found = false;
01736             foreach(map_location &new_loc, leaders_adj_v){
01737                 if(new_loc == loc){
01738                     found = true;
01739                     break;
01740                 }
01741             }
01742             if(!found){
01743                 leaders_adj_v.push_back(loc);
01744             }
01745         }
01746     }
01747     //leader_adj_count = leaders_adj_v.size();
01748 
01749 
01750     for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
01751         if (i->side() == get_side() &&
01752             i->movement_left() == i->total_movement() &&
01753             //leaders.find(*i) == leaders.end() && //unit_map::const_iterator(i) != leader &&
01754             std::find(leaders.begin(), leaders.end(), i) == leaders.end() &&
01755             !i->incapacitated())
01756         {
01757             // This unit still has movement left, and is a candidate to retreat.
01758             // We see the amount of power of each side on the situation,
01759             // and decide whether it should retreat.
01760             if(should_retreat(i->get_location(), i, fullmove_srcdst, fullmove_dstsrc, get_caution())) {
01761 
01762                 bool can_reach_leader = false;
01763 
01764                 // Time to retreat. Look for the place where the power balance
01765                 // is most in our favor.
01766                 // If we can't find anywhere where we like the power balance,
01767                 // just try to get to the best defensive hex.
01768                 typedef move_map::const_iterator Itor;
01769                 std::pair<Itor,Itor> itors = get_srcdst().equal_range(i->get_location());
01770                 map_location best_pos, best_defensive(i->get_location());
01771 
01772                 double best_rating = -1000.0;
01773                 int best_defensive_rating = i->defense_modifier(resources::game_map->get_terrain(i->get_location()))
01774                     - (resources::game_map->is_village(i->get_location()) ? 10 : 0);
01775                 while(itors.first != itors.second) {
01776 
01777                     //if(leader != units_.end() && std::count(leader_adj,
01778                     //          leader_adj + 6, itors.first->second)) {
01779                     if(std::find(leaders_adj_v.begin(), leaders_adj_v.end(), itors.first->second) != leaders_adj_v.end()){
01780 
01781                         can_reach_leader = true;
01782                         break;
01783                     }
01784 
01785                     // We rate the power balance of a hex based on our power projection
01786                     // compared to theirs, multiplying their power projection by their
01787                     // chance to hit us on the hex we're planning to flee to.
01788                     const map_location& hex = itors.first->second;
01789                     const int defense = i->defense_modifier(resources::game_map->get_terrain(hex));
01790                     const double our_power = power_projection(hex,get_dstsrc());
01791                     const double their_power = power_projection(hex,get_enemy_dstsrc()) * double(defense)/100.0;
01792                     const double rating = our_power - their_power;
01793                     if(rating > best_rating) {
01794                         best_pos = hex;
01795                         best_rating = rating;
01796                     }
01797 
01798                     // Give a bonus for getting to a village.
01799                     const int modified_defense = defense - (resources::game_map->is_village(hex) ? 10 : 0);
01800 
01801                     if(modified_defense < best_defensive_rating) {
01802                         best_defensive_rating = modified_defense;
01803                         best_defensive = hex;
01804                     }
01805 
01806                     ++itors.first;
01807                 }
01808 
01809                 // If the unit is in range of its leader, it should
01810                 // never retreat -- it has to defend the leader instead.
01811                 if(can_reach_leader) {
01812                     continue;
01813                 }
01814 
01815                 if(!best_pos.valid()) {
01816                     best_pos = best_defensive;
01817                 }
01818 
01819                 if(best_pos.valid()) {
01820                     move_ = check_move_action(i->get_location(), best_pos, true);
01821                     if (move_->is_ok()) {
01822                         return get_score();
01823                     }
01824                 }
01825             }
01826         }
01827     }
01828 
01829     return BAD_SCORE;
01830 }
01831 
01832 void retreat_phase::execute()
01833 {
01834     move_->execute();
01835     if (!move_->is_ok()){
01836         LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
01837     }
01838 }
01839 
01840 
01841 
01842 bool retreat_phase::should_retreat(const map_location& loc, const unit_map::const_iterator& un,  const move_map &srcdst, const move_map &dstsrc, double caution)
01843 {
01844     const move_map &enemy_dstsrc = get_enemy_dstsrc();
01845 
01846     if(caution <= 0.0) {
01847         return false;
01848     }
01849 
01850     double optimal_terrain = best_defensive_position(un->get_location(), dstsrc,
01851             srcdst, enemy_dstsrc).chance_to_hit/100.0;
01852     const double proposed_terrain =
01853         un->defense_modifier(resources::game_map->get_terrain(loc)) / 100.0;
01854 
01855     // The 'exposure' is the additional % chance to hit
01856     // this unit receives from being on a sub-optimal defensive terrain.
01857     const double exposure = proposed_terrain - optimal_terrain;
01858 
01859     const double our_power = power_projection(loc,dstsrc);
01860     const double their_power = power_projection(loc,enemy_dstsrc);
01861     return caution*their_power*(1.0+exposure) > our_power;
01862 }
01863 
01864 
01865 //==============================================================
01866 
01867 simple_move_and_targeting_phase::simple_move_and_targeting_phase(
01868         rca_context &context, const config &cfg)
01869     : candidate_action(context,cfg)
01870     , move_()
01871 {
01872 }
01873 
01874 simple_move_and_targeting_phase::~simple_move_and_targeting_phase()
01875 {
01876 }
01877 
01878 double simple_move_and_targeting_phase::evaluate()
01879 {
01880     unit_map &units_ = *resources::units;
01881 
01882     unit_map::const_iterator leader = units_.find_leader(get_side());
01883     map_location my_leader_loc = map_location::null_location;
01884     if (leader.valid()) {
01885         my_leader_loc = leader->get_location();
01886     }
01887 
01888     for(leader = units_.begin(); leader != units_.end(); ++leader) {
01889         if(leader->can_recruit() && current_team().is_enemy(leader->side())) {
01890             break;
01891         }
01892     }
01893 
01894     if(leader == units_.end()) {
01895         return BAD_SCORE;
01896     }
01897 
01898     int closest_distance = -1;
01899     std::pair<map_location,map_location> closest_move;
01900 
01901     for(move_map::const_iterator i = get_dstsrc().begin(); i != get_dstsrc().end(); ++i) {
01902         if(my_leader_loc == i->first && get_passive_leader()){//passive_leader
01903             continue;
01904         }
01905         int distance = distance_between(i->first, leader->get_location());
01906         if(closest_distance == -1 || distance < closest_distance) {
01907             if ((i->second!=my_leader_loc) && (i->second!=i->first)) {
01908                 closest_distance = distance;
01909                 closest_move = *i;
01910             }
01911         }
01912     }
01913 
01914     if(closest_distance != -1) {
01915         move_ = check_move_action(closest_move.second,closest_move.first,true);
01916         if (move_->is_ok()){
01917             return get_score();
01918         }
01919     }
01920 
01921     return BAD_SCORE;
01922 }
01923 
01924 void simple_move_and_targeting_phase::execute()
01925 {
01926     move_->execute();
01927     if (!move_->is_ok()){
01928         LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
01929     }
01930 }
01931 
01932 
01933 //==============================================================
01934 
01935 leader_control_phase::leader_control_phase( rca_context &context, const config &cfg )
01936     : candidate_action(context,cfg)
01937 {
01938 }
01939 
01940 
01941 leader_control_phase::~leader_control_phase()
01942 {
01943 }
01944 
01945 double leader_control_phase::evaluate()
01946 {
01947     ERR_AI_TESTING_AI_DEFAULT << get_name() << ": evaluate - not yet implemented" << std::endl;
01948     return BAD_SCORE;
01949 }
01950 
01951 
01952 
01953 void leader_control_phase::execute()
01954 {
01955     ERR_AI_TESTING_AI_DEFAULT << get_name() << ": execute - not yet implemented" << std::endl;
01956 }
01957 
01958 //==============================================================
01959 
01960 passive_leader_shares_keep_phase::passive_leader_shares_keep_phase( rca_context &context, const config &cfg )
01961     :candidate_action(context, cfg)
01962 {
01963 }
01964 
01965 
01966 passive_leader_shares_keep_phase::~passive_leader_shares_keep_phase()
01967 {
01968 }
01969 
01970 double passive_leader_shares_keep_phase::evaluate()
01971 {
01972     if(get_passive_leader() && !get_passive_leader_shares_keep()){
01973         return BAD_SCORE;
01974     }
01975     std::vector<unit_map::unit_iterator> ai_leaders = resources::units->find_leaders(get_side());
01976     bool allied_leaders_available = false;
01977     foreach(team &tmp_team, *resources::teams){
01978         if(!current_team().is_enemy(tmp_team.side())){
01979             std::vector<unit_map::unit_iterator> allied_leaders = resources::units->find_leaders(get_side());
01980             if (!allied_leaders.empty()){
01981                 allied_leaders_available = true;
01982                 break;
01983             }
01984         }
01985     }
01986     if(allied_leaders_available){
01987         return get_score();
01988     }
01989     return BAD_SCORE;
01990 }
01991 
01992 void passive_leader_shares_keep_phase::execute()
01993 {
01994     //get all AI leaders
01995     std::vector<unit_map::unit_iterator> ai_leaders = resources::units->find_leaders(get_side());
01996 
01997     //calculate all possible moves (AI + allies)
01998     typedef std::map<map_location, pathfind::paths> path_map;
01999     path_map possible_moves;
02000     move_map friends_srcdst, friends_dstsrc;
02001     calculate_moves(*resources::units, possible_moves, friends_srcdst, friends_dstsrc, false, true);
02002 
02003     //check for each ai leader if he should move away from his keep
02004     foreach(unit_map::unit_iterator &ai_leader, ai_leaders){
02005         //only if leader is on a keep
02006         if (!resources::game_map->is_keep(ai_leader->get_location())) {
02007             continue;
02008         }
02009         const map_location &keep = ai_leader->get_location();
02010         map_location recruit_loc = find_vacant_tile(*resources::game_map, *resources::units, keep, pathfind::VACANT_CASTLE);
02011         if(!resources::game_map->on_board(recruit_loc)){
02012             continue;
02013         }
02014         bool friend_can_reach_keep = false;
02015 
02016         //for each leader, check if he's allied and can reach our keep
02017         for(path_map::const_iterator i = possible_moves.begin(); i != possible_moves.end(); ++i){
02018             const unit_map::const_iterator itor = resources::units->find(i->first);
02019             team &leader_team = (*resources::teams)[itor->side() - 1];
02020             if(itor != resources::units->end() && itor->can_recruit() && itor->side() != get_side() && (leader_team.total_income() + leader_team.gold() > leader_team.minimum_recruit_price())){
02021                 pathfind::paths::dest_vect::const_iterator tokeep = i->second.destinations.find(keep);
02022                 if(tokeep != i->second.destinations.end()){
02023                     friend_can_reach_keep = true;
02024                     break;
02025                 }
02026             }
02027         }
02028         //if there's no allied leader who can reach the keep, check next ai leader
02029         if(friend_can_reach_keep){
02030             //determine the best place the ai leader can move to
02031             map_location best_move;
02032             int defense_modifier = 100;
02033             for(pathfind::paths::dest_vect::const_iterator i = possible_moves[keep].destinations.begin()
02034                     ; i != possible_moves[keep].destinations.end()
02035                     ; ++i){
02036 
02037                 if(distance_between(i->curr, keep) <= 3){
02038                     int tmp_def_mod = ai_leader->defense_modifier(resources::game_map->get_terrain(i->curr));
02039                     if(tmp_def_mod < defense_modifier){
02040                         defense_modifier = tmp_def_mod;
02041                         best_move = i->curr;
02042                     }
02043                 }
02044             }
02045             //only move if there's a place with a good defense
02046             if(defense_modifier < 100){
02047                 move_result_ptr move = check_move_action(keep, best_move, true);
02048                 if(move->is_ok()){
02049                     move->execute();
02050                     if (!move->is_ok()){
02051                         LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
02052                     }else{
02053                         ai_leader->set_goto(keep);
02054                     }
02055                 }else{
02056                     LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
02057                 }
02058             }
02059         }
02060         ai_leader->remove_movement_ai();
02061     }
02062     foreach(unit_map::unit_iterator &leader, ai_leaders){
02063         leader->remove_movement_ai();
02064     }
02065     //ERR_AI_TESTING_AI_DEFAULT << get_name() << ": evaluate - not yet implemented" << std::endl;
02066 }
02067 
02068 
02069 //==============================================================
02070 
02071 
02072 } //end of namespace testing_ai_default
02073 
02074 } //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:32 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs