00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "global.hpp"
00020
00021 #include "ai.hpp"
00022 #include "attack_prediction.hpp"
00023 #include "game_config.hpp"
00024 #include "gamestatus.hpp"
00025 #include "log.hpp"
00026
00027 #include <cassert>
00028
00029 #define LOG_AI LOG_STREAM(info, ai)
00030
00031 const int max_positions = 10000;
00032
00033
00034 void ai::do_attack_analysis(
00035 const location& loc,
00036 const move_map& srcdst, const move_map& dstsrc,
00037 const move_map& fullmove_srcdst, const move_map& fullmove_dstsrc,
00038 const move_map& enemy_srcdst, const move_map& enemy_dstsrc,
00039 const location* tiles, bool* used_locations,
00040 std::vector<location>& units,
00041 std::vector<attack_analysis>& result,
00042 attack_analysis& cur_analysis
00043 )
00044 {
00045
00046 raise_user_interact();
00047
00048 if(cur_analysis.movements.size() >= size_t(attack_depth())) {
00049
00050 return;
00051 }
00052
00053 static double best_results[6];
00054 if(result.empty()) {
00055 for(int i = 0; i != 6; ++i) {
00056 best_results[i] = 0.0;
00057 }
00058 }
00059
00060 const size_t max_positions = 1000;
00061 if(result.size() > max_positions && !cur_analysis.movements.empty()) {
00062 LOG_AI << "cut analysis short with number of positions\n";
00063 return;
00064 }
00065
00066 const double cur_rating = cur_analysis.movements.empty() ? -1.0 :
00067 cur_analysis.rating(current_team().aggression(),*this);
00068
00069 double rating_to_beat = cur_rating;
00070
00071 if(!cur_analysis.movements.empty()) {
00072 assert(cur_analysis.movements.size() < 6);
00073 double& best_res = best_results[cur_analysis.movements.size()-1];
00074 rating_to_beat = best_res = maximum(best_res,cur_rating);
00075 }
00076
00077 for(size_t i = 0; i != units.size(); ++i) {
00078 const location current_unit = units[i];
00079
00080 unit_map::iterator unit_itor = units_.find(current_unit);
00081 assert(unit_itor != units_.end());
00082
00083
00084
00085
00086
00087
00088 bool backstab = false, slow = false;
00089 std::vector<attack_type>& attacks = unit_itor->second.attacks();
00090 for(std::vector<attack_type>::iterator a = attacks.begin(); a != attacks.end(); ++a) {
00091 a->set_specials_context(gamemap::location(),gamemap::location(),
00092 &units_,&map_,&state_,&teams_,true,NULL);
00093 if(a->get_special_bool("backstab")) {
00094 backstab = true;
00095 }
00096
00097 if(a->get_special_bool("slow")) {
00098 slow = true;
00099 }
00100 }
00101
00102 if(slow && cur_analysis.movements.empty() == false) {
00103 continue;
00104 }
00105
00106
00107
00108
00109
00110
00111 bool is_surrounded = false;
00112 bool is_flanked = false;
00113 int enemy_units_around = 0;
00114 int accessible_tiles = 0;
00115 gamemap::location adj[6];
00116 get_adjacent_tiles(current_unit, adj);
00117
00118 size_t tile;
00119 for(tile = 0; tile != 3; ++tile) {
00120
00121 const unit_map::const_iterator tmp_unit = units_.find(adj[tile]);
00122 bool possible_flanked = false;
00123
00124 if(map_.on_board(adj[tile]))
00125 {
00126 accessible_tiles++;
00127 if(tmp_unit != units_.end() && team_num_ != tmp_unit->second.side())
00128 {
00129 enemy_units_around++;
00130 possible_flanked = true;
00131 }
00132 }
00133
00134 const unit_map::const_iterator tmp_opposite_unit = units_.find(adj[tile + 3]);
00135 if(map_.on_board(adj[tile + 3]))
00136 {
00137 accessible_tiles++;
00138 if(tmp_opposite_unit != units_.end() && team_num_ != tmp_opposite_unit->second.side())
00139 {
00140 enemy_units_around++;
00141 if(possible_flanked)
00142 {
00143 is_flanked = true;
00144 }
00145 }
00146 }
00147 }
00148
00149 if((is_flanked && enemy_units_around > 2) || enemy_units_around >= accessible_tiles - 1)
00150 is_surrounded = true;
00151
00152
00153
00154 double best_vulnerability = 0.0, best_support = 0.0;
00155 int best_rating = 0;
00156 int cur_position = -1;
00157
00158
00159 for(int j = 0; j != 6; ++j) {
00160
00161
00162 if(used_locations[j]) {
00163 continue;
00164 }
00165
00166
00167 if (tiles[j] != current_unit) {
00168 typedef std::multimap<location,location>::const_iterator Itor;
00169 std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
00170 while(its.first != its.second) {
00171 if(its.first->second == current_unit)
00172 break;
00173 ++its.first;
00174 }
00175
00176
00177 if(its.first == its.second || units_.find(tiles[j]) != units_.end()) {
00178 continue;
00179 }
00180 }
00181
00182
00183 int backstab_bonus = 1;
00184 double surround_bonus = 1.0;
00185
00186 if(tiles[(j+3)%6] != current_unit) {
00187 const unit_map::const_iterator itor = units_.find(tiles[(j+3)%6]);
00188
00189
00190
00191
00192
00193
00194
00195
00196 if(itor != units_.end() &&
00197 backstab_check(tiles[j], loc, units_, teams_)) {
00198 if(backstab) {
00199 backstab_bonus = 2;
00200 }
00201
00202 surround_bonus = 1.2;
00203 }
00204
00205
00206 }
00207
00208
00209 const int rating = rate_terrain(unit_itor->second,tiles[j]) * backstab_bonus;
00210 if(cur_position >= 0 && rating < best_rating) {
00211 continue;
00212 }
00213
00214
00215 const double vulnerability = power_projection(tiles[j],enemy_dstsrc);
00216
00217
00218
00219
00220 const double support = power_projection(tiles[j],fullmove_dstsrc,false);
00221
00222
00223
00224 if(cur_position >= 0 && rating == best_rating && vulnerability/surround_bonus - support*surround_bonus >= best_vulnerability - best_support) {
00225 continue;
00226 }
00227
00228 cur_position = j;
00229 best_rating = rating;
00230 best_vulnerability = vulnerability/surround_bonus;
00231 best_support = support*surround_bonus;
00232 }
00233
00234 if(cur_position != -1) {
00235 units.erase(units.begin() + i);
00236
00237 cur_analysis.movements.push_back(std::pair<location,location>(current_unit,tiles[cur_position]));
00238
00239 cur_analysis.vulnerability += best_vulnerability;
00240
00241 cur_analysis.support += best_support;
00242
00243 cur_analysis.is_surrounded = is_surrounded;
00244
00245 cur_analysis.analyze(map_, units_, teams_, state_, *this, dstsrc, srcdst, enemy_dstsrc, current_team().aggression());
00246
00247
00248 if(cur_analysis.rating(current_team().aggression(),*this) > rating_to_beat) {
00249
00250 result.push_back(cur_analysis);
00251 used_locations[cur_position] = true;
00252 do_attack_analysis(loc,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
00253 tiles,used_locations,
00254 units,result,cur_analysis);
00255 used_locations[cur_position] = false;
00256 }
00257
00258 cur_analysis.vulnerability -= best_vulnerability;
00259 cur_analysis.support -= best_support;
00260
00261 cur_analysis.movements.pop_back();
00262
00263 units.insert(units.begin() + i, current_unit);
00264 }
00265 }
00266 }
00267
00268
00269 void ai::attack_analysis::analyze(const gamemap& map, unit_map& units,
00270 const std::vector<team>& teams,
00271 const gamestatus& status,
00272 class ai& ai_obj,
00273 const move_map& dstsrc, const move_map& srcdst,
00274 const move_map& enemy_dstsrc, double aggression)
00275 {
00276 const unit_map::const_iterator defend_it = units.find(target);
00277 assert(defend_it != units.end());
00278
00279
00280 gamemap::location adj[6];
00281 get_adjacent_tiles(target,adj);
00282 size_t tile;
00283 for(tile = 0; tile != 6; ++tile) {
00284 const unit_map::const_iterator leader = units.find(adj[tile]);
00285 if(leader != units.end() && leader->second.can_recruit() && ai_obj.current_team().is_enemy(leader->second.side()) == false) {
00286 break;
00287 }
00288 }
00289
00290 leader_threat = (tile != 6);
00291 uses_leader = false;
00292
00293 target_value = defend_it->second.cost();
00294 target_value += (double(defend_it->second.experience())/
00295 double(defend_it->second.max_experience()))*target_value;
00296 target_starting_damage = defend_it->second.max_hitpoints() -
00297 defend_it->second.hitpoints();
00298
00299
00300
00301
00302
00303 alternative_terrain_quality = 0.0;
00304 double cost_sum = 0.0;
00305 for(size_t i = 0; i != movements.size(); ++i) {
00306 const unit_map::const_iterator att = units.find(movements[i].first);
00307 const double cost = att->second.cost();
00308 cost_sum += cost;
00309 alternative_terrain_quality += cost*ai_obj.best_defensive_position(movements[i].first,dstsrc,srcdst,enemy_dstsrc).chance_to_hit;
00310 }
00311 alternative_terrain_quality /= cost_sum*100;
00312
00313 avg_damage_inflicted = 0.0;
00314 avg_damage_taken = 0.0;
00315 resources_used = 0.0;
00316 terrain_quality = 0.0;
00317 avg_losses = 0.0;
00318 chance_to_kill = 0.0;
00319
00320 double def_avg_experience = 0.0;
00321 double first_chance_kill = 0.0;
00322
00323 double prob_dead_already = 0.0;
00324 assert(!movements.empty());
00325 std::vector<std::pair<location,location> >::const_iterator m;
00326
00327 battle_context *prev_bc = NULL;
00328 const combatant *prev_def = NULL;
00329
00330 for (m = movements.begin(); m != movements.end(); ++m) {
00331
00332 std::pair<gamemap::location,unit> *up = units.extract(m->first);
00333 up->first = m->second;
00334 units.add(up);
00335
00336 if (up->second.can_recruit()) {
00337 uses_leader = true;
00338 leader_threat = false;
00339 }
00340
00341 int att_weapon = -1, def_weapon = -1;
00342 bool from_cache = false;
00343 battle_context *bc;
00344
00345
00346
00347 std::map<std::pair<location, const unit_type *>,std::pair<battle_context::unit_stats,battle_context::unit_stats> >::iterator usc;
00348 if(up->second.type()) {
00349 usc = ai_obj.unit_stats_cache_.find(std::pair<location, const unit_type *>(target, up->second.type()));
00350 } else {
00351 usc = ai_obj.unit_stats_cache_.end();
00352 }
00353
00354 if (usc != ai_obj.unit_stats_cache_.end() &&
00355 usc->second.first.attack_num <
00356 static_cast<int>(up->second.attacks().size())) {
00357
00358 from_cache = true;
00359 bc = new battle_context(usc->second.first, usc->second.second);
00360 } else {
00361 bc = new battle_context(map, teams, units, status, m->second, target, att_weapon, def_weapon, aggression, prev_def);
00362 }
00363 const combatant &att = bc->get_attacker_combatant(prev_def);
00364 const combatant &def = bc->get_defender_combatant(prev_def);
00365
00366 delete prev_bc;
00367 prev_bc = bc;
00368 prev_def = &bc->get_defender_combatant(prev_def);
00369
00370 if (!from_cache && up->second.type()) {
00371 ai_obj.unit_stats_cache_.insert(std::pair<std::pair<location, const unit_type *>,std::pair<battle_context::unit_stats,battle_context::unit_stats> >
00372 (std::pair<location, const unit_type *>(target, up->second.type()),
00373 std::pair<battle_context::unit_stats,battle_context::unit_stats>(bc->get_attacker_stats(),
00374 bc->get_defender_stats())));
00375 }
00376
00377
00378 double prob_fought = (1.0 - prob_dead_already);
00379
00380
00381 double prob_killed = def.hp_dist[0] - prob_dead_already;
00382 prob_dead_already = def.hp_dist[0];
00383
00384 double prob_died = att.hp_dist[0];
00385 double prob_survived = (1.0 - prob_died) * prob_fought;
00386
00387 double cost = up->second.cost();
00388 const bool on_village = map.is_village(m->second);
00389
00390 cost += (double(up->second.experience())/double(up->second.max_experience()))*cost;
00391 resources_used += cost;
00392 avg_losses += cost * prob_died;
00393
00394
00395 if (on_village) {
00396 avg_damage_taken -= game_config::poison_amount*2 * prob_survived;
00397 }
00398
00399 terrain_quality += (double(bc->get_defender_stats().chance_to_hit)/100.0)*cost * (on_village ? 0.5 : 1.0);
00400
00401 double advance_prob = 0.0;
00402
00403 if (!up->second.advances_to().empty()) {
00404 int xp_for_advance = up->second.max_experience() - up->second.experience();
00405 int kill_xp, fight_xp;
00406
00407
00408
00409 if (xp_for_advance <= 0)
00410 xp_for_advance = 1;
00411
00412 fight_xp = defend_it->second.level();
00413 kill_xp = fight_xp * game_config::kill_experience;
00414
00415 if (fight_xp >= xp_for_advance)
00416 advance_prob = prob_fought;
00417 else if (kill_xp >= xp_for_advance)
00418 advance_prob = prob_killed;
00419 avg_losses -= up->second.cost() * advance_prob;
00420
00421
00422
00423
00424
00425
00426
00427 avg_losses -= (up->second.cost()*fight_xp)/(xp_for_advance*4) * (prob_fought - prob_killed);
00428 avg_losses -= (up->second.cost()*kill_xp)/(xp_for_advance*4) * prob_killed;
00429
00430
00431
00432 if (bc->get_attacker_stats().plagues) {
00433 avg_losses -= prob_killed * up->second.cost();
00434 }
00435 }
00436
00437
00438 avg_damage_taken += (up->second.hitpoints() - att.average_hp()) * (1.0 - advance_prob);
00439
00440
00441
00442 def_avg_experience += up->second.level() *
00443 (1.0 - att.hp_dist[0] + game_config::kill_experience * att.hp_dist[0]);
00444 if (m == movements.begin()) {
00445 first_chance_kill = def.hp_dist[0];
00446 }
00447 }
00448
00449 if (!defend_it->second.advances_to().empty() &&
00450 def_avg_experience >= defend_it->second.max_experience() - defend_it->second.experience()) {
00451
00452 chance_to_kill = first_chance_kill;
00453
00454 avg_damage_inflicted = defend_it->second.hitpoints() - defend_it->second.max_hitpoints();
00455 } else {
00456 chance_to_kill = prev_def->hp_dist[0];
00457 avg_damage_inflicted = defend_it->second.hitpoints() - prev_def->average_hp(map.gives_healing(defend_it->first));
00458 }
00459
00460 delete prev_bc;
00461 terrain_quality /= resources_used;
00462
00463
00464 for (m = movements.begin(); m != movements.end(); ++m) {
00465 std::pair<gamemap::location,unit> *up = units.extract(m->second);
00466 up->first = m->first;
00467 units.add(up);
00468 }
00469 }
00470
00471 double ai::attack_analysis::rating(double aggression, ai& ai_obj) const
00472 {
00473 if(leader_threat) {
00474 aggression = 1.0;
00475 }
00476
00477
00478
00479 if(uses_leader && aggression > -4.0) {
00480 LOG_AI << "uses leader..\n";
00481 aggression = -4.0;
00482 }
00483
00484 double value = chance_to_kill*target_value - avg_losses*(1.0-aggression);
00485
00486 if(terrain_quality > alternative_terrain_quality) {
00487
00488
00489
00490
00491
00492 const double exposure_mod = uses_leader ? 2.0 : ai_obj.current_team().caution();
00493 const double exposure = exposure_mod*resources_used*(terrain_quality - alternative_terrain_quality)*vulnerability/maximum<double>(0.01,support);
00494 LOG_AI << "attack option has base value " << value << " with exposure " << exposure << ": "
00495 << vulnerability << "/" << support << " = " << (vulnerability/maximum<double>(support,0.1)) << "\n";
00496 if(uses_leader) {
00497 ai_obj.log_message("attack option has value " + str_cast(value) + " with exposure " + str_cast(exposure) + ": " + str_cast(vulnerability) + "/" + str_cast(support));
00498 }
00499
00500 value -= exposure*(1.0-aggression);
00501 }
00502
00503
00504
00505
00506 if(uses_leader && ai_obj.leader_can_reach_keep() && ai_obj.current_team().gold() > 20) {
00507 value -= double(ai_obj.current_team().gold())*0.5;
00508 }
00509
00510
00511 value += ((target_starting_damage/3 + avg_damage_inflicted) - (1.0-aggression)*avg_damage_taken)/10.0;
00512
00513
00514
00515
00516 if(!is_surrounded || (support != 0 && avg_damage_taken != 0))
00517 {
00518
00519
00520
00521 if(vulnerability > 50.0 && vulnerability > support*2.0
00522 && chance_to_kill < 0.02 && aggression < 0.75
00523 && !ai_obj.attack_close(target)) {
00524 return -1.0;
00525 }
00526 }
00527
00528 if(!leader_threat && vulnerability*terrain_quality > 0.0) {
00529 value *= support/(vulnerability*terrain_quality);
00530 }
00531
00532 value /= ((resources_used/2) + (resources_used/2)*terrain_quality);
00533
00534 if(leader_threat) {
00535 value *= 5.0;
00536 }
00537
00538 LOG_AI << "attack on " << target << ": attackers: " << movements.size()
00539 << " value: " << value << " chance to kill: " << chance_to_kill
00540 << " damage inflicted: " << avg_damage_inflicted
00541 << " damage taken: " << avg_damage_taken
00542 << " vulnerability: " << vulnerability
00543 << " support: " << support
00544 << " quality: " << terrain_quality
00545 << " alternative quality: " << alternative_terrain_quality << "\n";
00546
00547 return value;
00548 }
00549
00550 std::vector<ai::attack_analysis> ai::analyze_targets(
00551 const move_map& srcdst, const move_map& dstsrc,
00552 const move_map& enemy_srcdst, const move_map& enemy_dstsrc
00553 )
00554 {
00555 log_scope2(ai, "analyzing targets...");
00556
00557 std::vector<attack_analysis> res;
00558
00559 std::vector<location> unit_locs;
00560 for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
00561 if(i->second.side() == team_num_ && i->second.attacks_left()) {
00562 unit_locs.push_back(i->first);
00563 }
00564 }
00565
00566 bool used_locations[6];
00567 std::fill(used_locations,used_locations+6,false);
00568
00569 std::map<location,paths> dummy_moves;
00570 move_map fullmove_srcdst, fullmove_dstsrc;
00571 calculate_possible_moves(dummy_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
00572
00573 unit_stats_cache_.clear();
00574
00575 for(unit_map::const_iterator j = units_.begin(); j != units_.end(); ++j) {
00576
00577
00578
00579 if(current_team().is_enemy(j->second.side()) && !j->second.incapacitated() &&
00580 j->second.invisible(j->first,units_,teams_) == false) {
00581 location adjacent[6];
00582 get_adjacent_tiles(j->first,adjacent);
00583 attack_analysis analysis;
00584 analysis.target = j->first;
00585 analysis.vulnerability = 0.0;
00586 analysis.support = 0.0;
00587
00588
00589
00590 do_attack_analysis(j->first,srcdst,dstsrc,fullmove_srcdst,fullmove_dstsrc,enemy_srcdst,enemy_dstsrc,
00591 adjacent,used_locations,unit_locs,res,analysis);
00592
00593
00594
00595
00596
00597 }
00598 }
00599
00600 return res;
00601 }
00602
00603 double ai::power_projection(const gamemap::location& loc, const move_map& dstsrc, bool use_terrain) const
00604 {
00605 gamemap::location used_locs[6];
00606 int ratings[6];
00607 int num_used_locs = 0;
00608
00609 gamemap::location locs[6];
00610 get_adjacent_tiles(loc,locs);
00611
00612 const int lawful_bonus = state_.get_time_of_day().lawful_bonus;
00613
00614 int res = 0;
00615
00616 bool changed = false;
00617 for (int i = 0;; ++i) {
00618 if (i == 6) {
00619 if (!changed) break;
00620
00621
00622 changed = false;
00623 i = 0;
00624 }
00625
00626 if (map_.on_board(locs[i]) == false) {
00627 continue;
00628 }
00629
00630 const t_translation::t_terrain terrain = map_[locs[i]];
00631
00632 typedef move_map::const_iterator Itor;
00633 typedef std::pair<Itor,Itor> Range;
00634 Range its = dstsrc.equal_range(locs[i]);
00635
00636 gamemap::location* const beg_used = used_locs;
00637 gamemap::location* end_used = used_locs + num_used_locs;
00638
00639 int best_rating = 0;
00640 gamemap::location best_unit;
00641
00642 for(Itor it = its.first; it != its.second; ++it) {
00643 const unit_map::const_iterator u = units_.find(it->second);
00644
00645
00646 if(u == units_.end()) {
00647 continue;
00648 }
00649
00650 const unit& un = u->second;
00651
00652 int tod_modifier = 0;
00653 if(un.alignment() == unit_type::LAWFUL) {
00654 tod_modifier = lawful_bonus;
00655 } else if(un.alignment() == unit_type::CHAOTIC) {
00656 tod_modifier = -lawful_bonus;
00657 }
00658
00659 int hp = un.hitpoints() * 1000 / un.max_hitpoints();
00660 int most_damage = 0;
00661 for(std::vector<attack_type>::const_iterator att =
00662 un.attacks().begin(); att != un.attacks().end(); ++att) {
00663 int damage = att->damage() * att->num_attacks() *
00664 (100 + tod_modifier);
00665 if(damage > most_damage) {
00666 most_damage = damage;
00667 }
00668 }
00669
00670 int village_bonus = use_terrain && map_.is_village(terrain) ? 3 : 2;
00671 int defense = use_terrain ? 100 - un.defense_modifier(terrain) : 50;
00672 int rating = hp * defense * most_damage * village_bonus / 200;
00673 if(rating > best_rating) {
00674 gamemap::location *pos = std::find(beg_used, end_used, it->second);
00675
00676 if (pos == end_used || rating >= ratings[pos - beg_used]) {
00677 best_rating = rating;
00678 best_unit = it->second;
00679 }
00680 }
00681 }
00682
00683 if (!best_unit.valid()) continue;
00684 gamemap::location *pos = std::find(beg_used, end_used, best_unit);
00685 int index = pos - beg_used;
00686 if (index == num_used_locs)
00687 ++num_used_locs;
00688 else if (best_rating == ratings[index])
00689 continue;
00690 else {
00691
00692
00693 res -= ratings[index];
00694 changed = true;
00695 }
00696 used_locs[index] = best_unit;
00697 ratings[index] = best_rating;
00698 res += best_rating;
00699 }
00700
00701 return res / 100000.;
00702 }
00703
00704
00705
00706 bool ai::desperate_attack(const gamemap::location &loc)
00707 {
00708 const unit &u = units_.find(loc)->second;
00709 LOG_AI << "desperate attack by '" << u.type_id() << "' " << loc << "\n";
00710
00711 gamemap::location adj[6];
00712 get_adjacent_tiles(loc, adj);
00713
00714 double best_kill_prob = 0.0;
00715 unsigned int best_weapon = 0;
00716 int best_def_weapon = -1;
00717 unsigned best_dir = 0;
00718
00719 {
00720 for(unsigned int n = 0; n != 6; ++n) {
00721 const unit_map::iterator enemy = find_visible_unit(units_,adj[n], map_, teams_, current_team());
00722 if (enemy != units_.end() &&
00723 current_team().is_enemy(enemy->second.side()) && !enemy->second.incapacitated()) {
00724 const std::vector<attack_type>& attacks = u.attacks();
00725 for (unsigned int i = 0; i != attacks.size(); ++i) {
00726
00727 if (attacks[i].attack_weight() > 0) {
00728 battle_context bc(map_, teams_, units_, state_, loc, adj[n], i);
00729 combatant att(bc.get_attacker_stats());
00730 combatant def(bc.get_defender_stats());
00731 att.fight(def);
00732 if (def.hp_dist[0] > best_kill_prob) {
00733 best_kill_prob = def.hp_dist[0];
00734 best_weapon = i;
00735 best_def_weapon = bc.get_defender_stats().attack_num;
00736 best_dir = n;
00737 }
00738 }
00739 }
00740 }
00741 }
00742 }
00743
00744 if (best_kill_prob > 0.0) {
00745 attack_enemy(loc, adj[best_dir], best_weapon, best_def_weapon);
00746 return true;
00747 }
00748
00749 double least_hp = u.hitpoints() + 1;
00750
00751 {
00752
00753 for (unsigned int n = 0; n != 6; ++n) {
00754 const unit_map::iterator enemy = find_visible_unit(units_,adj[n], map_, teams_, current_team());
00755 if (enemy != units_.end() &&
00756 current_team().is_enemy(enemy->second.side()) && !enemy->second.incapacitated()) {
00757
00758 const std::vector<attack_type>& attacks = units_.find(adj[n])->second.attacks();
00759 for (unsigned int i = 0; i != attacks.size(); ++i) {
00760
00761 if (attacks[i].attack_weight() > 0) {
00762 battle_context bc(map_, teams_, units_, state_, adj[n], loc, i);
00763 combatant att(bc.get_attacker_stats());
00764 combatant def(bc.get_defender_stats());
00765 att.fight(def);
00766 if (def.average_hp() < least_hp) {
00767 least_hp = def.average_hp();
00768 best_dir = n;
00769 }
00770 }
00771 }
00772 }
00773 }
00774 }
00775
00776
00777 if (least_hp != u.hitpoints() + 1) {
00778 battle_context bc(map_, teams_, units_, state_, loc, adj[best_dir], -1, -1, 0.5);
00779 attack_enemy(loc, adj[best_dir], bc.get_attacker_stats().attack_num,
00780 bc.get_defender_stats().attack_num);
00781 return true;
00782 }
00783 return false;
00784 }