00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
00080 if(ui->can_recruit() && get_passive_leader() && !get_passive_leader_shares_keep()){
00081 continue;
00082 }
00083
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
00148
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
00196
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
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
00222
00223
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
00247
00248
00249
00250 const int villages_per_scout = get_villages_per_scout()/2;
00251
00252
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
00278
00279 if (options.empty()) {
00280 options.push_back("");
00281 }
00282
00283 while (recruit_usage(options[rand()%options.size()])) {
00284
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
00303 unit_types.build_all(unit_type::HELP_INDEX);
00304
00305 std::vector<std::string> options;
00306 bool found = false;
00307
00308
00309 foreach (const std::string &name, current_team().recruits())
00310 {
00311 const unit_type *ut = unit_types.find(name);
00312 if (!ut) continue;
00313
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
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
00344
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
00357
00358
00359
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
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
00385
00386
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
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
00414 if (steadfast && resistance < 100)
00415 resistance = std::max<int>(resistance * 2 - 100, 50);
00416
00417 int cth = i->get_special_bool("chance_to_hit", true) ? 70 : defense;
00418 int weight = i->damage() * i->num_attacks();
00419
00420 if (living && cth != 0 && i->get_special_bool("poison", true)) {
00421
00422 int prob = 100;
00423 for (int j = 0; j < i->num_attacks(); ++j)
00424 prob = prob * (100 - cth);
00425
00426 weight += game_config::poison_amount * (100 - prob) / 100;
00427 }
00428 sum += cth * resistance * weight * weight;
00429 weight_sum += weight;
00430 }
00431
00432
00433 sum /= std::max<int>(1,std::min<int>(a.hitpoints(),1000));
00434
00435
00436
00437
00438
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
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
00506
00507
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();
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
00581
00582
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
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
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_);
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
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
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
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
00776
00777 std::multimap<int,map_location> moves_toward_keep;
00778
00779
00780
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
00793
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
00849
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
00872
00873
00874
00875
00876
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
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
00971
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 ¤t_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
01017
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
01038
01039
01040 if(un.hitpoints() < (threat*2*un.defense_modifier(map_.get_terrain(current_loc)))/100) {
01041 continue;
01042 }
01043
01044
01045
01046 std::multimap<map_location, map_location>::const_iterator next = j;
01047 ++next;
01048 const bool at_begin = (j == dstsrc.begin());
01049 std::multimap<map_location, map_location>::const_iterator prev = j;
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
01079
01080
01081
01082
01083
01084
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
01133
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
01158 if(reachmap.empty()) {
01159
01160 return false;
01161 }
01162
01163 if(reachmap.size() == 1) {
01164
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
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
01189 std::map<map_location ,
01190 std::vector<map_location > >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
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
01220
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
01268
01269 const size_t unit_count = reachmap.size();
01270
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
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
01288
01289 std::multimap<size_t ,
01290 size_t > unit_lookup;
01291
01292 std::vector<std::vector<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
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();
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
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
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
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
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
01367 std::multimap<size_t , size_t >
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
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
01391 if(matched == 2) {
01392
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
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
01414 reachmap.erase(units[src_itor->second]);
01415 reachmap.erase(units[dst_itor->second]);
01416
01417
01418 if(perfect) {
01419
01420
01421
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
01427
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
01441
01442
01443
01444
01445
01446
01447 std::vector<std::pair<map_location, map_location> > best_result;
01448
01449
01450
01451
01452
01453
01454
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
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
01486 std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
01487
01488
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
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
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
01526 std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
01527
01528
01529
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
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
01569 std::copy(best_result.begin(), best_result.end(), std::back_inserter(moves));
01570
01571
01572
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
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
01646
01647
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
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
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
01676
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
01714 const unit_map& units_ = *resources::units;
01715
01716
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
01726
01727
01728
01729
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
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
01754 std::find(leaders.begin(), leaders.end(), i) == leaders.end() &&
01755 !i->incapacitated())
01756 {
01757
01758
01759
01760 if(should_retreat(i->get_location(), i, fullmove_srcdst, fullmove_dstsrc, get_caution())) {
01761
01762 bool can_reach_leader = false;
01763
01764
01765
01766
01767
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
01778
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
01786
01787
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
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
01810
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
01856
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()){
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
01995 std::vector<unit_map::unit_iterator> ai_leaders = resources::units->find_leaders(get_side());
01996
01997
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
02004 foreach(unit_map::unit_iterator &ai_leader, ai_leaders){
02005
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
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
02029 if(friend_can_reach_keep){
02030
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
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
02066 }
02067
02068
02069
02070
02071
02072 }
02073
02074 }