The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ca.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2016 by Yurii Chernyi <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * Default AI (Testing)
17  * @file
18  */
19 
20 #include "ai/default/ca.hpp"
21 #include "ai/actions.hpp"
22 #include "ai/manager.hpp"
23 #include "ai/composite/engine.hpp"
24 #include "ai/composite/rca.hpp"
25 #include "ai/composite/stage.hpp"
26 #include "game_board.hpp"
27 #include "game_classification.hpp"
28 #include "game_data.hpp"
29 #include "log.hpp"
30 #include "map/map.hpp"
31 #include "resources.hpp"
32 #include "team.hpp"
33 #include "units/unit.hpp"
34 #include "pathfind/pathfind.hpp"
35 #include "pathfind/teleport.hpp"
36 
37 #include <numeric>
38 
39 #include <SDL.h>
40 
41 static lg::log_domain log_ai_testing_ai_default("ai/ca/testing_ai_default");
42 #define DBG_AI_TESTING_AI_DEFAULT LOG_STREAM(debug, log_ai_testing_ai_default)
43 #define LOG_AI_TESTING_AI_DEFAULT LOG_STREAM(info, log_ai_testing_ai_default)
44 #define WRN_AI_TESTING_AI_DEFAULT LOG_STREAM(warn, log_ai_testing_ai_default)
45 #define ERR_AI_TESTING_AI_DEFAULT LOG_STREAM(err, log_ai_testing_ai_default)
46 
47 
48 namespace ai {
49 
50 namespace ai_default_rca {
51 
52 //==============================================================
53 
54 goto_phase::goto_phase( rca_context &context, const config &cfg )
55  : candidate_action(context,cfg)
56  , move_()
57 {
58 }
59 
61 {
62 }
63 
65 {
66  // Execute goto-movements - first collect gotos in a list
67  std::vector<map_location> gotos;
70 
71  for(unit_map::iterator ui = units_.begin(); ui != units_.end(); ++ui) {
72  if (ui->get_goto() == ui->get_location()) {
73  ui->set_goto(map_location());
74  } else if (ui->side() == get_side() && map_.on_board(ui->get_goto())) {
75  gotos.push_back(ui->get_location());
76  }
77  }
78 
79  for(std::vector<map_location>::const_iterator g = gotos.begin(); g != gotos.end(); ++g) {
80  unit_map::const_iterator ui = units_.find(*g);
81  // passive_leader: never moves or attacks
82  if(ui->can_recruit() && get_passive_leader() && !get_passive_leader_shares_keep()){
83  continue;//@todo: only bail out if goto is on keep
84  }
85  // end of passive_leader
86 
88 
90 
92  route = pathfind::a_star_search(ui->get_location(), ui->get_goto(), 10000.0, &calc, map_.w(), map_.h(), &allowed_teleports);
93 
94  if (!route.steps.empty()){
95  move_ = check_move_action(ui->get_location(), route.steps.back(), true, true);
96  } else {
97  // there is no direct path (yet)
98  // go to the nearest hex instead.
99  // maybe a door will open later or something
100 
101  int closest_distance = -1;
102  std::pair<map_location,map_location> closest_move;
103  for(move_map::const_iterator i = get_dstsrc().begin(); i != get_dstsrc().end(); ++i) {
104  if(i->second != ui->get_location()) {
105  continue;
106  }
107  int distance = distance_between(i->first,ui->get_goto());
108  if(closest_distance == -1 || distance < closest_distance) {
109  closest_distance = distance;
110  closest_move = *i;
111  }
112  }
113  if(closest_distance != -1) {
114  move_ = check_move_action(ui->get_location(), closest_move.first);
115  } else {
116  continue;
117  }
118  }
119 
120  if (move_->is_ok()) {
121  return get_score();
122  }
123  }
124 
125  return BAD_SCORE;
126 }
127 
129 {
130  if (!move_) {
131  return;
132  }
133 
134  move_->execute();
135  if (!move_->is_ok()){
136  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
137  }
138 
139  // In some situations, a theoretically possible path is blocked by allies,
140  // resulting in the unit not moving. In this case, we remove all remaining
141  // movement from the unit in order to prevent blacklisting of the CA.
142  if (!move_->is_gamestate_changed()){
143  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute did not move unit; removing moves instead" << std::endl;
144  stopunit_result_ptr stopunit = check_stopunit_action(move_->get_unit_location(), true, false);
145  stopunit->execute();
146  }
147 }
148 
149 
150 //==============================================================
151 
153  : candidate_action(context,cfg),best_analysis_(),choice_rating_(-1000.0)
154 {
155 }
156 
158 {
159 }
160 
162 {
163  std::vector<std::string> options = get_recruitment_pattern();
164 
165  choice_rating_ = -1000.0;
166  int ticks = SDL_GetTicks();
167 
168  const std::vector<attack_analysis> analysis = get_attacks(); //passive_leader: in aspect_attacks::analyze_targets()
169 
170  int time_taken = SDL_GetTicks() - ticks;
171  LOG_AI_TESTING_AI_DEFAULT << "took " << time_taken << " ticks for " << analysis.size()
172  << " positions. Analyzing...\n";
173 
174  ticks = SDL_GetTicks();
175 
176  const int max_sims = 50000;
177  int num_sims = analysis.empty() ? 0 : max_sims/analysis.size();
178  if(num_sims < 20)
179  num_sims = 20;
180  if(num_sims > 40)
181  num_sims = 40;
182 
183  LOG_AI_TESTING_AI_DEFAULT << "simulations: " << num_sims << "\n";
184 
185  const int max_positions = 30000;
186  const int skip_num = analysis.size()/max_positions;
187 
188  std::vector<attack_analysis>::const_iterator choice_it = analysis.end();
189  for(std::vector<attack_analysis>::const_iterator it = analysis.begin();
190  it != analysis.end(); ++it) {
191 
192  if(skip_num > 0 && ((it - analysis.begin())%skip_num) && it->movements.size() > 1)
193  continue;
194 
195  const double rating = it->rating(get_aggression(),*this);
196  LOG_AI_TESTING_AI_DEFAULT << "attack option rated at " << rating << " ("
197  << (it->uses_leader ? get_leader_aggression() : get_aggression()) << ")\n";
198 
199  if(rating > choice_rating_) {
200  choice_it = it;
201  choice_rating_ = rating;
202  }
203  }
204 
205  time_taken = SDL_GetTicks() - ticks;
206  LOG_AI_TESTING_AI_DEFAULT << "analysis took " << time_taken << " ticks\n";
207 
208 
209  // suokko tested the rating against current_team().caution()
210  // Bad mistake -- the AI became extremely reluctant to attack anything.
211  // Documenting this in case someone has this bright idea again...*don't*...
212  if(choice_rating_ > 0.0) {
213  best_analysis_ = *choice_it;
214  return get_score();
215  } else {
216  return BAD_SCORE;
217  }
218 }
219 
221 {
222  assert(choice_rating_ > 0.0);
223  map_location from = best_analysis_.movements[0].first;
224  map_location to = best_analysis_.movements[0].second;
225  map_location target_loc = best_analysis_.target;
226 
227  if (from!=to) {
228  move_result_ptr move_res = execute_move_action(from,to,false);
229  if (!move_res->is_ok()) {
230  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok, move failed" << std::endl;
231  return;
232  }
233  }
234 
235  attack_result_ptr attack_res = check_attack_action(to, target_loc, -1);
236  if (!attack_res->is_ok()) {
237  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok, attack cancelled" << std::endl;
238  } else {
239  attack_res->execute();
240  if (!attack_res->is_ok()) {
241  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok, attack failed" << std::endl;
242  }
243  }
244 
245 }
246 
247 //==============================================================
248 
250  : candidate_action(context,cfg), auto_remove_(), dst_(), id_(), move_()
251 {
252 }
253 
255 {
256 }
257 
259 {
260 
261  const config &goal = get_leader_goal();
262  //passive leader can reach a goal
263  if (!goal) {
264  LOG_AI_TESTING_AI_DEFAULT << get_name() << "No goal found\n";
265  return BAD_SCORE;
266  }
267 
268  if (goal.empty()) {
269  LOG_AI_TESTING_AI_DEFAULT << get_name() << "Empty goal found\n";
270  return BAD_SCORE;
271  }
272 
273  double max_risk = goal["max_risk"].to_double(1 - get_caution());
274  auto_remove_ = goal["auto_remove"].to_bool();
275 
277  if (!dst_.valid()) {
278  ERR_AI_TESTING_AI_DEFAULT << "Invalid goal: "<<std::endl<<goal;
279  return BAD_SCORE;
280  }
281 
283  if (!leader.valid() || leader->incapacitated()) {
284  WRN_AI_TESTING_AI_DEFAULT << "Leader not found" << std::endl;
285  return BAD_SCORE;
286  }
287 
288  id_ = goal["id"].str();
289  if (leader->get_location() == dst_) {
290  //goal already reached
291  if (auto_remove_ && !id_.empty()) {
292  remove_goal(id_);
293  } else {
294  move_ = check_move_action(leader->get_location(), leader->get_location(), !auto_remove_);//we do full moves if we don't want to remove goal
295  if (move_->is_ok()) {
296  return get_score();
297  } else {
298  return BAD_SCORE;
299  }
300  }
301  }
302 
304  pathfind::plain_route route = a_star_search(leader->get_location(), dst_, 1000.0, &calc,
306  if(route.steps.empty()) {
307  LOG_AI_TESTING_AI_DEFAULT << "route empty";
308  return BAD_SCORE;
309  }
310 
311  const pathfind::paths leader_paths(*leader, false, true, current_team());
312 
313  std::map<map_location,pathfind::paths> possible_moves;
314  possible_moves.insert(std::pair<map_location,pathfind::paths>(leader->get_location(), leader_paths));
315 
316  map_location loc;
317  for (const map_location &l : route.steps)
318  {
319  if (leader_paths.destinations.contains(l) &&
320  power_projection(l, get_enemy_dstsrc()) < leader->hitpoints() * max_risk)
321  {
322  loc = l;
323  }
324  }
325 
326  if(loc.valid()) {
327  move_ = check_move_action(leader->get_location(), loc, false);
328  if (move_->is_ok()) {
329  return get_score();
330  }
331  }
332  return BAD_SCORE;
333 
334 }
335 
337 {
338  move_->execute();
339  if (!move_->is_ok()){
340  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
341  }
342  if (move_->get_unit_location()==dst_) {
343  //goal already reached
344  if (auto_remove_ && !id_.empty()) {
345  remove_goal(id_);
346  }
347  }
348 }
349 
351 {
352  config mod_ai;
353  mod_ai["side"] = get_side();
354  mod_ai["path"] = "aspect[leader_goal].facet["+id+"]";
355  mod_ai["action"] = "delete";
357 }
358 
359 //==============================================================
360 
362  : candidate_action(context,cfg),move_()
363 {
364 
365 }
366 
368 {
369 
370 }
371 
373 {
374  if (get_leader_ignores_keep()) {
375  return BAD_SCORE;
376  }
378  return BAD_SCORE;
379  }
380 
381  // 1. Collect all leaders in a list
382  // 2. Get the suitable_keep for each leader
383  // 3. Choose the leader with the nearest suitable_keep (and which still have moves)
384  // 4. If leader can reach this keep in 1 turn -> set move_ to there
385  // 5. If not -> Calculate the best move_ (use a-star search)
386  // 6. Save move_ for execution
387 
388  // 1.
389  const unit_map &units_ = *resources::units;
390  const std::vector<unit_map::const_iterator> leaders = units_.find_leaders(get_side());
391  if (leaders.empty()) {
392  return BAD_SCORE;
393  }
394 
395  // 2. + 3.
396  const unit* best_leader = nullptr;
397  map_location best_keep;
398  int shortest_distance = 99999;
399 
400  for (const unit_map::const_iterator& leader : leaders) {
401  if (leader->incapacitated() || leader->movement_left() == 0) {
402  continue;
403  }
404 
405  // Find where the leader can move
406  const ai::moves_map &possible_moves = get_possible_moves();
407  const ai::moves_map::const_iterator& p_it = possible_moves.find(leader->get_location());
408  if (p_it == possible_moves.end()) {
409  return BAD_SCORE;
410  }
411  const pathfind::paths leader_paths = p_it->second;
412 
413  const map_location& keep = suitable_keep(leader->get_location(), leader_paths);
414  if (keep == map_location::null_location() || keep == leader->get_location()) {
415  continue;
416  }
417 
419 
420  const pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(*leader, current_team());
421 
422  pathfind::plain_route route;
423  route = pathfind::a_star_search(leader->get_location(), keep, 10000.0, &calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
424 
425  if (!route.steps.empty() || route.move_cost < shortest_distance) {
426  best_leader = &(*leader);
427  best_keep = keep;
428  shortest_distance = route.move_cost;
429  }
430  }
431 
432  if (best_leader == nullptr) {
433  return BAD_SCORE;
434  }
435 
436  // 4.
437  const unit* leader = best_leader;
438  const map_location keep = best_keep;
439  const pathfind::paths leader_paths(*leader, false, true, current_team());
441  const pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(*leader, current_team());
442 
443  if (leader_paths.destinations.contains(keep) && units_.count(keep) == 0) {
444  move_ = check_move_action(leader->get_location(), keep, false);
445  if (move_->is_ok()) {
446  return get_score();
447  }
448  }
449 
450  // 5.
451  // The leader can't move to his keep, try to move to the closest location
452  // to the keep where there are no enemies in range.
453  // Make a map of the possible locations the leader can move to,
454  // ordered by the distance from the keep.
455  typedef std::multimap<int, map_location> ordered_locations;
456  ordered_locations moves_toward_keep;
457 
458  pathfind::plain_route route;
459  route = pathfind::a_star_search(leader->get_location(), keep, 10000.0, &calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
460 
461  // find next hop
463  int next_hop_cost = 0;
464  for (const map_location& step : route.steps) {
465  if (leader_paths.destinations.contains(step)) {
466  next_hop = step;
467  next_hop_cost += leader->movement_cost(resources::gameboard->map().get_terrain(step));
468  } else {
469  break;
470  }
471  }
472  if (next_hop == map_location::null_location()) {
473  return BAD_SCORE;
474  }
475  //define the next hop to have the lowest cost (0)
476  moves_toward_keep.insert(std::make_pair(0, next_hop));
477 
478  for (const pathfind::paths::step &dest : leader_paths.destinations) {
479  if (!units_.find(dest.curr).valid()) {
480  route = pathfind::a_star_search(dest.curr, next_hop, 10000.0, &calc,
481  resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
482  if (route.move_cost < next_hop_cost) {
483  moves_toward_keep.insert(std::make_pair(route.move_cost, dest.curr));
484  }
485  }
486  }
487 
488  // Find the first location which we can move to,
489  // without the threat of enemies.
490  for (const ordered_locations::value_type& pair : moves_toward_keep) {
491  const map_location& loc = pair.second;
492  if (get_enemy_dstsrc().count(loc) == 0) {
493  move_ = check_move_action(leader->get_location(), loc, true);
494  if (move_->is_ok()) {
495  return get_score();
496  }
497  }
498  }
499  return BAD_SCORE;
500 }
501 
503 {
504  move_->execute();
505  if (!move_->is_ok()) {
506  LOG_AI_TESTING_AI_DEFAULT << get_name() <<"::execute not ok" << std::endl;
507  }
508 }
509 
510 //==============================================================
511 
513  : candidate_action(context,cfg)
514  , keep_loc_()
515  , leader_loc_()
516  , best_leader_loc_()
517  , debug_(false)
518  , moves_()
519 {
520 }
521 
523 {
524 }
525 
527 {
528  moves_.clear();
531  if (!moves_.empty()) {
532  return get_score();
533  }
534  return BAD_SCORE;
535 }
536 
537 
539 {
541  unit_map::const_iterator leader = units_.find_leader(get_side());
542  // Move all the units to get villages, however move the leader last,
543  // so that the castle will be cleared if it wants to stop to recruit along the way.
544  std::pair<map_location,map_location> leader_move;
545 
546  for(tmoves::const_iterator i = moves_.begin(); i != moves_.end(); ++i) {
547 
548  if(leader != units_.end() && leader->get_location() == i->second) {
549  leader_move = *i;
550  } else {
551  if (resources::gameboard->find_visible_unit(i->first, current_team()) == units_.end()) {
552  move_result_ptr move_res = execute_move_action(i->second,i->first,true);
553  if (!move_res->is_ok()) {
554  return;
555  }
556 
557  const map_location loc = move_res->get_unit_location();
558  leader = units_.find_leader(get_side());
559  const unit_map::const_iterator new_unit = units_.find(loc);
560 
561  if (new_unit != units_.end() &&
562  power_projection(i->first, get_enemy_dstsrc()) >= new_unit->hitpoints() / 4)
563  {
564  LOG_AI_TESTING_AI_DEFAULT << "found support target... " << new_unit->get_location() << '\n';
565  //FIXME: suokko tweaked the constant 1.0 to the formula:
566  //25.0* current_team().caution() * power_projection(loc,enemy_dstsrc) / new_unit->second.hitpoints()
567  //Is this an improvement?
568 
569  ///@todo 1.7 check if this an improvement
570  //add_target(target(new_unit->first,1.0,target::SUPPORT));
571  }
572  }
573  }
574  }
575 
576  if(leader_move.second.valid()) {
577  if((resources::gameboard->find_visible_unit(leader_move.first , current_team()) == units_.end())
578  && resources::gameboard->map().is_village(leader_move.first)) {
579  move_result_ptr move_res = execute_move_action(leader_move.second,leader_move.first,true);
580  if (!move_res->is_ok()) {
581  return;
582  }
583  }
584  }
585 
586  return;
587 }
588 
590  const move_map& dstsrc, const move_map& enemy_dstsrc,
591  unit_map::const_iterator &leader)
592 {
593  DBG_AI_TESTING_AI_DEFAULT << "deciding which villages we want...\n";
595  const int ticks = SDL_GetTicks();
597  if(leader != units_.end()) {
598  keep_loc_ = nearest_keep(leader->get_location());
599  leader_loc_ = leader->get_location();
600  } else {
603  }
604 
606 
607  // Find our units who can move.
608  treachmap reachmap;
609  for(unit_map::const_iterator u_itor = units_.begin();
610  u_itor != units_.end(); ++u_itor) {
611  if(u_itor->can_recruit() && get_passive_leader()){
612  continue;
613  }
614  if(u_itor->side() == get_side() && u_itor->movement_left()) {
615  reachmap.insert(std::make_pair(u_itor->get_location(), std::vector<map_location>()));
616  }
617  }
618 
619 
620  DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units found who can try to capture a village.\n";
621 
622  find_villages(reachmap, moves_, dstsrc, enemy_dstsrc);
623 
624  treachmap::iterator itor = reachmap.begin();
625  while(itor != reachmap.end()) {
626  if(itor->second.size() == 0) {
627  itor = remove_unit(reachmap, moves_, itor);
628  } else {
629  ++itor;
630  }
631  }
632 
633  if(!reachmap.empty()) {
634  DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units left after removing the ones who "
635  "can't reach a village, send the to the dispatcher.\n";
636 
637  dump_reachmap(reachmap);
638 
639  dispatch(reachmap, moves_);
640  } else {
641  DBG_AI_TESTING_AI_DEFAULT << "No more units left after removing the ones who can't reach a village.\n";
642  }
643 
644  LOG_AI_TESTING_AI_DEFAULT << "Village assignment done: " << (SDL_GetTicks() - ticks)
645  << " ms, resulted in " << moves_.size() << " units being dispatched.\n";
646 
647 }
648 
650  treachmap& reachmap,
651  tmoves& moves,
652  const std::multimap<map_location,map_location>& dstsrc,
653  const std::multimap<map_location,map_location>& enemy_dstsrc)
654 
655 {
656  std::map<map_location, double> vulnerability;
657 
658  const bool passive_leader = get_passive_leader();
659 
660  size_t min_distance = 100000;
661  const gamemap &map_ = resources::gameboard->map();
662  std::vector<team> &teams_ = *resources::teams;
663 
664  // When a unit is dispatched we need to make sure we don't
665  // dispatch this unit a second time, so store them here.
666  std::vector<map_location> dispatched_units;
667  for(std::multimap<map_location, map_location>::const_iterator
668  j = dstsrc.begin();
669  j != dstsrc.end(); ++j) {
670 
671  const map_location &current_loc = j->first;
672 
673  if(j->second == leader_loc_) {
674  if(passive_leader) {
675  continue;
676  }
677 
678  const size_t distance = distance_between(keep_loc_, current_loc);
679  if(distance < min_distance) {
680  min_distance = distance;
681  best_leader_loc_ = current_loc;
682  }
683  }
684 
685  if(std::find(dispatched_units.begin(), dispatched_units.end(),
686  j->second) != dispatched_units.end()) {
687  continue;
688  }
689 
690  if(map_.is_village(current_loc) == false) {
691  continue;
692  }
693 
694  bool want_village = true, owned = false;
695  for(size_t n = 0; n != teams_.size(); ++n) {
696  owned = teams_[n].owns_village(current_loc);
697  if(owned && !current_team().is_enemy(n+1)) {
698  want_village = false;
699  }
700 
701  if(owned) {
702  break;
703  }
704  }
705 
706  if(want_village == false) {
707  continue;
708  }
709 
710  // If it is a neutral village, and we have no leader,
711  // then the village is of no use to us, and we don't want it.
712  if(!owned && leader_loc_ == map_location::null_location()) {
713  continue;
714  }
715 
716  double threat = 0.0;
717  const std::map<map_location,double>::const_iterator vuln = vulnerability.find(current_loc);
718  if(vuln != vulnerability.end()) {
719  threat = vuln->second;
720  } else {
721  threat = power_projection(current_loc,enemy_dstsrc);
722  vulnerability.insert(std::pair<map_location,double>(current_loc,threat));
723  }
724 
725  const unit_map::const_iterator u = resources::units->find(j->second);
726  if (u == resources::units->end() || u->get_state("guardian")) {
727  continue;
728  }
729 
730  const unit &un = *u;
731  //FIXME: suokko turned this 2:1 to 1.5:1.0.
732  //and dropped the second term of the multiplication. Is that better?
733  //const double threat_multipler = (current_loc == leader_loc?2:1) * current_team().caution() * 10;
734  if(un.hitpoints() < (threat*2*un.defense_modifier(map_.get_terrain(current_loc)))/100) {
735  continue;
736  }
737 
738  // If the next and previous destination differs from our current destination,
739  // we're the only one who can reach the village -> dispatch.
740  std::multimap<map_location, map_location>::const_iterator next = j;
741  ++next; // j + 1 fails
742  const bool at_begin = (j == dstsrc.begin());
743  std::multimap<map_location, map_location>::const_iterator prev = j; //FIXME seems not to work
744  if(!at_begin) {
745  --prev;
746  }
747 #if 1
748  if((next == dstsrc.end() || next->first != current_loc)
749  && (at_begin || prev->first != current_loc)) {
750 
751  move_result_ptr move_check_res = check_move_action(j->second,j->first,true);
752  if (move_check_res->is_ok()) {
753  DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << j->second << " to village " << j->first << '\n';
754  moves.push_back(std::make_pair(j->first, j->second));
755  }
756  reachmap.erase(j->second);
757  dispatched_units.push_back(j->second);
758  continue;
759  }
760 #endif
761  reachmap[j->second].push_back(current_loc);
762  }
763 
764  DBG_AI_TESTING_AI_DEFAULT << moves.size() << " units already dispatched, "
765  << reachmap.size() << " left to evaluate.\n";
766 }
767 
769 {
770  DBG_AI_TESTING_AI_DEFAULT << "Starting simple dispatch.\n";
771 
772  // we now have a list with units with the villages they can reach.
773  // keep trying the following steps as long as one of them changes
774  // the state.
775  // 1. Dispatch units who can reach 1 village (if more units can reach that
776  // village only one can capture it, so use the first in the list.)
777  // 2. Villages which can only be reached by one unit get that unit dispatched
778  // to them.
779  size_t village_count = 0;
780  bool dispatched = true;
781  while(dispatched) {
782  dispatched = false;
783 
784  if(dispatch_unit_simple(reachmap, moves)) {
785  dispatched = true;
786  } else {
787  if(reachmap.empty()) {
788  DBG_AI_TESTING_AI_DEFAULT << "dispatch_unit_simple() found a final solution.\n";
789  break;
790  } else {
791  DBG_AI_TESTING_AI_DEFAULT << "dispatch_unit_simple() couldn't dispatch more units.\n";
792  }
793  }
794 
795  if(dispatch_village_simple(reachmap, moves, village_count)) {
796  dispatched = true;
797  } else {
798  if(reachmap.empty()) {
799  DBG_AI_TESTING_AI_DEFAULT << "dispatch_village_simple() found a final solution.\n";
800  break;
801  } else {
802  DBG_AI_TESTING_AI_DEFAULT << "dispatch_village_simple() couldn't dispatch more units.\n";
803  }
804  }
805 
806  if(reachmap.size() != 0 && dispatched) {
807  DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " unit(s) left restarting simple dispatching.\n";
808 
809  dump_reachmap(reachmap);
810  }
811  }
812 
813  if(reachmap.size() == 0) {
814  DBG_AI_TESTING_AI_DEFAULT << "No units left after simple dispatcher.\n";
815  return;
816  }
817 
818  DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units left for complex dispatch with "
819  << village_count << " villages left.\n";
820 
821  dump_reachmap(reachmap);
822 
823  dispatch_complex(reachmap, moves, village_count);
824 }
825 
826 // Returns need further processing
827 // false Nothing has been modified or no units left
829 {
830  bool result = false;
831 
832  treachmap::iterator itor = reachmap.begin();
833  while(itor != reachmap.end()) {
834  if(itor->second.size() == 1) {
835  const map_location village = itor->second[0];
836  result = true;
837 
838  DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->first << " to village " << village << '\n';
839  moves.push_back(std::make_pair(village, itor->first));
840  reachmap.erase(itor++);
841 
842  if(remove_village(reachmap, moves, village)) {
843  itor = reachmap.begin();
844  }
845 
846  } else {
847  ++itor;
848  }
849  }
850 
851  // Test special cases.
852  if(reachmap.empty()) {
853  // We're done.
854  return false;
855  }
856 
857  if(reachmap.size() == 1) {
858  // One unit left.
859  DBG_AI_TESTING_AI_DEFAULT << "Dispatched _last_ unit at " << reachmap.begin()->first
860  << " to village " << reachmap.begin()->second[0] << '\n';
861 
862  moves.push_back(std::make_pair(
863  reachmap.begin()->second[0], reachmap.begin()->first));
864 
865  reachmap.clear();
866  // We're done.
867  return false;
868  }
869 
870  return result;
871 }
872 
874  treachmap& reachmap, tmoves& moves, size_t& village_count)
875 {
876 
877  bool result = false;
878  bool dispatched = true;
879  while(dispatched) {
880  dispatched = false;
881 
882  // build the reverse map
883  std::map<map_location /*village location*/,
884  std::vector<map_location /* units that can reach it*/> >reversemap;
885 
886  treachmap::const_iterator itor = reachmap.begin();
887  for(;itor != reachmap.end(); ++itor) {
888 
889  for(std::vector<map_location>::const_iterator
890  v_itor = itor->second.begin();
891  v_itor != itor->second.end(); ++v_itor) {
892 
893  reversemap[*v_itor].push_back(itor->first);
894 
895  }
896  }
897 
898  village_count = reversemap.size();
899 
900  itor = reversemap.begin();
901  while(itor != reversemap.end()) {
902  if(itor->second.size() == 1) {
903  // One unit can reach this village.
904  const map_location village = itor->first;
905  dispatched = true;
906  result = true;
907 
908  DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->second[0] << " to village " << itor->first << '\n';
909  moves.push_back(std::make_pair(itor->first, itor->second[0]));
910 
911  reachmap.erase(itor->second[0]);
912  remove_village(reachmap, moves, village);
913  // Get can go to some trouble to remove the unit from the other villages
914  // instead we abort this loop end do a full rebuild on the map.
915  break;
916  } else {
917  ++itor;
918  }
919  }
920  }
921 
922  return result;
923 }
924 
926  treachmap& reachmap, tmoves& moves, const map_location& village)
927 {
928  bool result = false;
929  treachmap::iterator itor = reachmap.begin();
930  while(itor != reachmap.end()) {
931  itor->second.erase(std::remove(itor->second.begin(), itor->second.end(), village), itor->second.end());
932  if(itor->second.empty()) {
933  result = true;
934  itor = remove_unit(reachmap, moves, itor);
935  } else {
936  ++itor;
937  }
938  }
939  return result;
940 }
941 
943  treachmap& reachmap, tmoves& moves, treachmap::iterator unit)
944 {
945  assert(unit->second.empty());
946 
947  if(unit->first == leader_loc_ && best_leader_loc_ != map_location::null_location()) {
948  DBG_AI_TESTING_AI_DEFAULT << "Dispatch leader at " << leader_loc_ << " closer to the keep at "
949  << best_leader_loc_ << '\n';
950 
951  moves.push_back(std::make_pair(best_leader_loc_, leader_loc_));
952  }
953 
954  reachmap.erase(unit++);
955  return unit;
956 }
957 
959  treachmap& reachmap, tmoves& moves, const size_t village_count)
960 {
961  // ***** ***** Init and dispatch if every unit can reach every village.
962 
963  const size_t unit_count = reachmap.size();
964  // The maximum number of villages we can capture with the available units.
965  const size_t max_result = unit_count < village_count ? unit_count : village_count;
966 
967  assert(unit_count >= 2 && village_count >= 2);
968 
969  // Every unit can reach every village.
970  if(unit_count == 2 && village_count == 2) {
971  DBG_AI_TESTING_AI_DEFAULT << "Every unit can reach every village for 2 units, dispatch them.\n";
972  full_dispatch(reachmap, moves);
973  return;
974  }
975 
976  std::vector<map_location> units(unit_count);
977  std::vector<size_t> villages_per_unit(unit_count);
978  std::vector<map_location> villages;
979  std::vector<size_t> units_per_village(village_count);
980 
981  // We want to test the units, the ones who can reach the least
982  // villages first so this is our lookup map.
983  std::multimap<size_t /* villages_per_unit value*/,
984  size_t /*villages_per_unit index*/> unit_lookup;
985 
986  std::vector</*unit*/std::vector</*village*/bool> >
987  matrix(reachmap.size(), std::vector<bool>(village_count, false));
988 
989  treachmap::const_iterator itor = reachmap.begin();
990  for(size_t u = 0; u < unit_count; ++u, ++itor) {
991  units[u] = itor->first;
992  villages_per_unit[u] = itor->second.size();
993  unit_lookup.insert(std::make_pair(villages_per_unit[u], u));
994 
995  assert(itor->second.size() >= 2);
996 
997  for(size_t v = 0; v < itor->second.size(); ++v) {
998 
999  size_t v_index;
1000  // find the index of the v in the villages
1001  std::vector<map_location>::const_iterator v_itor =
1002  std::find(villages.begin(), villages.end(), itor->second[v]);
1003  if(v_itor == villages.end()) {
1004  v_index = villages.size(); // will be the last element after push_back.
1005  villages.push_back(itor->second[v]);
1006  } else {
1007  v_index = v_itor - villages.begin();
1008  }
1009 
1010  units_per_village[v_index]++;
1011 
1012  matrix[u][v_index] = true;
1013  }
1014  }
1015  for(std::vector<size_t>::const_iterator upv_it = units_per_village.begin();
1016  upv_it != units_per_village.end(); ++upv_it) {
1017 
1018  assert(*upv_it >=2);
1019  }
1020 
1021  if(debug_) {
1022  // Print header
1023  std::cerr << "Reach matrix:\n\nvillage";
1024  size_t u, v;
1025  for(v = 0; v < village_count; ++v) {
1026  std::cerr << '\t' << villages[v];
1027  }
1028  std::cerr << "\ttotal\nunit\n";
1029 
1030  // Print data
1031  for(u = 0; u < unit_count; ++u) {
1032  std::cerr << units[u];
1033 
1034  for(size_t v = 0; v < village_count; ++v) {
1035  std::cerr << '\t' << matrix[u][v];
1036  }
1037  std::cerr << "\t" << villages_per_unit[u] << '\n';
1038  }
1039 
1040  // Print footer
1041  std::cerr << "total";
1042  for(v = 0; v < village_count; ++v) {
1043  std::cerr << '\t' << units_per_village[v];
1044  }
1045  std::cerr << '\n';
1046  }
1047 
1048  // Test the special case, everybody can reach all villages
1049  const bool reach_all = ((village_count == unit_count)
1050  && (std::accumulate(villages_per_unit.begin(), villages_per_unit.end(), size_t())
1051  == (village_count * unit_count)));
1052 
1053  if(reach_all) {
1054  DBG_AI_TESTING_AI_DEFAULT << "Every unit can reach every village, dispatch them\n";
1055  full_dispatch(reachmap, moves);
1056  reachmap.clear();
1057  return;
1058  }
1059 
1060  // ***** ***** Find a square
1061  std::multimap<size_t /* villages_per_unit value*/, size_t /*villages_per_unit index*/>
1062  ::const_iterator src_itor = unit_lookup.begin();
1063 
1064  while(src_itor != unit_lookup.end() && src_itor->first == 2) {
1065 
1066  for(std::multimap<size_t, size_t>::const_iterator
1067  dst_itor = unit_lookup.begin();
1068  dst_itor != unit_lookup.end(); ++ dst_itor) {
1069 
1070  // avoid comparing us with ourselves.
1071  if(src_itor == dst_itor) {
1072  continue;
1073  }
1074 
1075  std::vector<bool> result;
1076  std::transform(matrix[src_itor->second].begin(), matrix[src_itor->second].end(),
1077  matrix[dst_itor->second].begin(),
1078  std::back_inserter(result),
1079  std::logical_and<bool>()
1080  );
1081 
1082  size_t matched = std::count(result.begin(), result.end(), true);
1083 
1084  // we found a solution, dispatch
1085  if(matched == 2) {
1086  // Collect data
1087  std::vector<bool>::iterator first = std::find(result.begin(), result.end(), true);
1088  std::vector<bool>::iterator second = std::find(first + 1, result.end(), true);
1089 
1090  const map_location village1 = villages[first - result.begin()];
1091  const map_location village2 = villages[second - result.begin()];
1092 
1093  const bool perfect = (src_itor->first == 2 &&
1094  dst_itor->first == 2 &&
1095  units_per_village[first - result.begin()] == 2 &&
1096  units_per_village[second - result.begin()] == 2);
1097 
1098  // Dispatch
1099  DBG_AI_TESTING_AI_DEFAULT << "Found a square.\nDispatched unit at " << units[src_itor->second]
1100  << " to village " << village1 << '\n';
1101  moves.push_back(std::make_pair(village1, units[src_itor->second]));
1102 
1103  DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << units[dst_itor->second]
1104  << " to village " << village2 << '\n';
1105  moves.push_back(std::make_pair(village2, units[dst_itor->second]));
1106 
1107  // Remove the units
1108  reachmap.erase(units[src_itor->second]);
1109  reachmap.erase(units[dst_itor->second]);
1110 
1111  // Evaluate and start correct function.
1112  if(perfect) {
1113  // We did a perfect dispatch 2 units who could visit 2 villages.
1114  // This means we didn't change the assertion for this functions
1115  // so call ourselves recursively, and finish afterwards.
1116  DBG_AI_TESTING_AI_DEFAULT << "Perfect dispatch, do complex again.\n";
1117  dispatch_complex(reachmap, moves, village_count - 2);
1118  return;
1119  } else {
1120  // We did a not perfect dispatch but we did modify things
1121  // so restart dispatching.
1122  DBG_AI_TESTING_AI_DEFAULT << "NON Perfect dispatch, do dispatch again.\n";
1123  remove_village(reachmap, moves, village1);
1124  remove_village(reachmap, moves, village2);
1125  dispatch(reachmap, moves);
1126  return;
1127  }
1128  }
1129  }
1130 
1131  ++src_itor;
1132  }
1133 
1134  // ***** ***** Do all permutations.
1135  // Now walk through all possible permutations
1136  // - test whether the suggestion is possible
1137  // - does it result in max_villages
1138  // - dispatch and ready
1139  // - is it's result better as the last best
1140  // - store
1141  std::vector<std::pair<map_location, map_location> > best_result;
1142 
1143  // Bruteforcing all possible permutations can result in a slow game.
1144  // So there needs to be a balance between the best possible result and
1145  // not too slow. From the test (at the end of the file) a good number is
1146  // picked. In general we shouldn't reach this point too often if we do
1147  // there are a lot of villages which are unclaimed and a lot of units
1148  // to claim them.
1149  const size_t max_options = 8;
1150  if(unit_count >= max_options && village_count >= max_options) {
1151 
1152  DBG_AI_TESTING_AI_DEFAULT << "Too many units " << unit_count << " and villages "
1153  << village_count<<" found, evaluate only the first "
1154  << max_options << " options;\n";
1155 
1156  std::vector<size_t> perm (max_options, 0);
1157  for(size_t i =0; i < max_options; ++i) {
1158  perm[i] = i;
1159  }
1160  while(std::next_permutation(perm.begin(), perm.end())) {
1161 
1162  // Get result for current permutation.
1163  std::vector<std::pair<map_location,map_location> > result;
1164  for(size_t u = 0; u < max_options; ++u) {
1165  if(matrix[u][perm[u]]) {
1166  result.push_back(std::make_pair(villages[perm[u]], units[u]));
1167 
1168  }
1169  }
1170  if(result.size() == max_result) {
1171  best_result.swap(result);
1172  break;
1173  }
1174 
1175  if(result.size() > best_result.size()) {
1176  best_result.swap(result);
1177  }
1178  }
1179  // End of loop no optimal found, assign the best
1180  moves.insert(moves.end(), best_result.begin(), best_result.end());
1181 
1182  // Clean up the reachmap for dispatched units.
1183  for(std::vector<std::pair<map_location, map_location> >::const_iterator
1184  itor = best_result.begin(); itor != best_result.end(); ++itor) {
1185  reachmap.erase(itor->second);
1186  }
1187 
1188  // Try to dispatch whatever is left
1189  dispatch(reachmap, moves);
1190  return;
1191 
1192  } else if(unit_count <= village_count) {
1193 
1194  DBG_AI_TESTING_AI_DEFAULT << "Unit major\n";
1195 
1196  std::vector<size_t> perm (unit_count, 0);
1197  for(size_t i =0; i < unit_count; ++i) {
1198  perm[i] = i;
1199  }
1200  while(std::next_permutation(perm.begin(), perm.end())) {
1201  // Get result for current permutation.
1202  std::vector<std::pair<map_location,map_location> > result;
1203  for(size_t u = 0; u < unit_count; ++u) {
1204  if(matrix[u][perm[u]]) {
1205  result.push_back(std::make_pair(villages[perm[u]], units[u]));
1206 
1207  }
1208  }
1209  if(result.size() == max_result) {
1210  moves.insert(moves.end(), result.begin(), result.end());
1211  reachmap.clear();
1212  return;
1213  }
1214 
1215  if(result.size() > best_result.size()) {
1216  best_result.swap(result);
1217  }
1218  }
1219  // End of loop no optimal found, assign the best
1220  moves.insert(moves.end(), best_result.begin(), best_result.end());
1221 
1222  // clean up the reachmap we need to test whether the leader is still there
1223  // and if so remove him manually to get him dispatched.
1224  for(std::vector<std::pair<map_location, map_location> >::const_iterator
1225  itor = best_result.begin(); itor != best_result.end(); ++itor) {
1226  reachmap.erase(itor->second);
1227  }
1228  treachmap::iterator unit = reachmap.find(leader_loc_);
1229  if(unit != reachmap.end()) {
1230  unit->second.clear();
1231  remove_unit(reachmap, moves, unit);
1232  }
1233  reachmap.clear();
1234 
1235  } else {
1236 
1237  DBG_AI_TESTING_AI_DEFAULT << "Village major\n";
1238 
1239  std::vector<size_t> perm (village_count, 0);
1240  for(size_t i =0; i < village_count; ++i) {
1241  perm[i] = i;
1242  }
1243  while(std::next_permutation(perm.begin(), perm.end())) {
1244  // Get result for current permutation.
1245  std::vector<std::pair<map_location,map_location> > result;
1246  for(size_t v = 0; v < village_count; ++v) {
1247  if(matrix[perm[v]][v]) {
1248  result.push_back(std::make_pair(villages[v], units[perm[v]]));
1249 
1250  }
1251  }
1252  if(result.size() == max_result) {
1253  moves.insert(moves.end(), result.begin(), result.end());
1254  reachmap.clear();
1255  return;
1256  }
1257 
1258  if(result.size() > best_result.size()) {
1259  best_result.swap(result);
1260  }
1261  }
1262  // End of loop no optimal found, assigne the best
1263  moves.insert(moves.end(), best_result.begin(), best_result.end());
1264 
1265  // clean up the reachmap we need to test whether the leader is still there
1266  // and if so remove him manually to get him dispatched.
1267  for(std::vector<std::pair<map_location, map_location> >::const_iterator
1268  itor = best_result.begin(); itor != best_result.end(); ++itor) {
1269  reachmap.erase(itor->second);
1270  }
1271  treachmap::iterator unit = reachmap.find(leader_loc_);
1272  if(unit != reachmap.end()) {
1273  unit->second.clear();
1274  remove_unit(reachmap, moves, unit);
1275  }
1276  reachmap.clear();
1277  }
1278 }
1279 
1281 {
1282  treachmap::const_iterator itor = reachmap.begin();
1283  for(size_t i = 0; i < reachmap.size(); ++i, ++itor) {
1284  DBG_AI_TESTING_AI_DEFAULT << "Dispatched unit at " << itor->first
1285  << " to village " << itor->second[i] << '\n';
1286  moves.push_back(std::make_pair(itor->second[i], itor->first));
1287  }
1288 }
1289 
1291 {
1292  if(!debug_) {
1293  return;
1294  }
1295 
1296  for(treachmap::const_iterator itor =
1297  reachmap.begin(); itor != reachmap.end(); ++itor) {
1298 
1299  std::cerr << "Reachlist for unit at " << itor->first;
1300 
1301  if(itor->second.empty()) {
1302  std::cerr << "\tNone";
1303  }
1304 
1305  for(std::vector<map_location>::const_iterator
1306  v_itor = itor->second.begin();
1307  v_itor != itor->second.end(); ++v_itor) {
1308 
1309  std::cerr << '\t' << *v_itor;
1310  }
1311  std::cerr << '\n';
1312 
1313  }
1314 }
1315 
1316 //==============================================================
1317 
1319  : candidate_action(context,cfg),move_()
1320 {
1321 }
1322 
1324 {
1325 }
1326 
1328 {
1329  // Find units in need of healing.
1331  unit_map::iterator u_it = units_.begin();
1332  for(; u_it != units_.end(); ++u_it) {
1333  unit &u = *u_it;
1334 
1335  if(u.can_recruit() && get_passive_leader()){
1336  continue;
1337  }
1338 
1339  // If the unit is on our side, has lost as many or more than
1340  // 1/2 round worth of healing, and doesn't regenerate itself,
1341  // then try to find a vacant village for it to rest in.
1342  if(u.side() == get_side() &&
1345  !u.get_ability_bool("regenerate"))
1346  {
1347  // Look for the village which is the least vulnerable to enemy attack.
1348  typedef std::multimap<map_location,map_location>::const_iterator Itor;
1349  std::pair<Itor,Itor> it = get_srcdst().equal_range(u_it->get_location());
1350  double best_vulnerability = 100000.0;
1351  // Make leader units more unlikely to move to vulnerable villages
1352  const double leader_penalty = (u.can_recruit()?2.0:1.0);
1353  Itor best_loc = it.second;
1354  while(it.first != it.second) {
1355  const map_location& dst = it.first->second;
1356  if (resources::gameboard->map().gives_healing(dst) && (units_.find(dst) == units_.end() || dst == u_it->get_location())) {
1357  const double vuln = power_projection(dst, get_enemy_dstsrc());
1358  DBG_AI_TESTING_AI_DEFAULT << "found village with vulnerability: " << vuln << "\n";
1359  if(vuln < best_vulnerability) {
1360  best_vulnerability = vuln;
1361  best_loc = it.first;
1362  DBG_AI_TESTING_AI_DEFAULT << "chose village " << dst << '\n';
1363  }
1364  }
1365 
1366  ++it.first;
1367  }
1368 
1369  // If we have found an eligible village,
1370  // and we can move there without expecting to get whacked next turn:
1371  if(best_loc != it.second && best_vulnerability*leader_penalty < u.hitpoints()) {
1372  move_ = check_move_action(best_loc->first,best_loc->second,true);
1373  if (move_->is_ok()) {
1374  return get_score();
1375  }
1376  }
1377  }
1378  }
1379 
1380  return BAD_SCORE;
1381 }
1382 
1384 {
1385  LOG_AI_TESTING_AI_DEFAULT << "moving unit to village for healing...\n";
1386  move_->execute();
1387  if (!move_->is_ok()){
1388  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
1389  }
1390 }
1391 
1392 //==============================================================
1393 
1395  : candidate_action(context,cfg), move_()
1396 {
1397 }
1398 
1400 {
1401 }
1402 
1404 {
1405 
1406 
1407  // Get versions of the move map that assume that all units are at full movement
1408  const unit_map& units_ = *resources::units;
1409 
1410  //unit_map::const_iterator leader = units_.find_leader(get_side());
1411  std::vector<unit_map::const_iterator> leaders = units_.find_leaders(get_side());
1412  std::map<map_location,pathfind::paths> dummy_possible_moves;
1413 
1414  move_map fullmove_srcdst;
1415  move_map fullmove_dstsrc;
1416  calculate_possible_moves(dummy_possible_moves, fullmove_srcdst, fullmove_dstsrc,
1417  false, true, &get_avoid());
1418 
1419  /*map_location leader_adj[6];
1420  if(leader != units_.end()) {
1421  get_adjacent_tiles(leader->get_location(), leader_adj);
1422  }*/
1423  //int leader_adj_count = 0;
1424  std::vector<map_location> leaders_adj_v;
1425  for (unit_map::const_iterator leader : leaders) {
1426  map_location tmp_leader_adj[6];
1427  get_adjacent_tiles(leader->get_location(), tmp_leader_adj);
1428  for (map_location &loc : tmp_leader_adj) {
1429  bool found = false;
1430  for (map_location &new_loc : leaders_adj_v) {
1431  if(new_loc == loc){
1432  found = true;
1433  break;
1434  }
1435  }
1436  if(!found){
1437  leaders_adj_v.push_back(loc);
1438  }
1439  }
1440  }
1441  //leader_adj_count = leaders_adj_v.size();
1442 
1443 
1444  for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
1445  if (i->side() == get_side() &&
1446  i->movement_left() == i->total_movement() &&
1447  //leaders.find(*i) == leaders.end() && //unit_map::const_iterator(i) != leader &&
1448  std::find(leaders.begin(), leaders.end(), i) == leaders.end() &&
1449  !i->incapacitated())
1450  {
1451  // This unit still has movement left, and is a candidate to retreat.
1452  // We see the amount of power of each side on the situation,
1453  // and decide whether it should retreat.
1454  if(should_retreat(i->get_location(), i, fullmove_srcdst, fullmove_dstsrc, get_caution())) {
1455 
1456  bool can_reach_leader = false;
1457 
1458  // Time to retreat. Look for the place where the power balance
1459  // is most in our favor.
1460  // If we can't find anywhere where we like the power balance,
1461  // just try to get to the best defensive hex.
1462  typedef move_map::const_iterator Itor;
1463  std::pair<Itor,Itor> itors = get_srcdst().equal_range(i->get_location());
1464  map_location best_pos, best_defensive(i->get_location());
1465 
1466  double best_rating = -1000.0;
1467  int best_defensive_rating = i->defense_modifier(resources::gameboard->map().get_terrain(i->get_location()))
1468  - (resources::gameboard->map().is_village(i->get_location()) ? 10 : 0);
1469  while(itors.first != itors.second) {
1470 
1471  //if(leader != units_.end() && std::count(leader_adj,
1472  // leader_adj + 6, itors.first->second)) {
1473  if(std::find(leaders_adj_v.begin(), leaders_adj_v.end(), itors.first->second) != leaders_adj_v.end()){
1474 
1475  can_reach_leader = true;
1476  break;
1477  }
1478 
1479  // We rate the power balance of a hex based on our power projection
1480  // compared to theirs, multiplying their power projection by their
1481  // chance to hit us on the hex we're planning to flee to.
1482  const map_location& hex = itors.first->second;
1483  const int defense = i->defense_modifier(resources::gameboard->map().get_terrain(hex));
1484  const double our_power = power_projection(hex,get_dstsrc());
1485  const double their_power = power_projection(hex,get_enemy_dstsrc()) * double(defense)/100.0;
1486  const double rating = our_power - their_power;
1487  if(rating > best_rating) {
1488  best_pos = hex;
1489  best_rating = rating;
1490  }
1491 
1492  // Give a bonus for getting to a village.
1493  const int modified_defense = defense - (resources::gameboard->map().is_village(hex) ? 10 : 0);
1494 
1495  if(modified_defense < best_defensive_rating) {
1496  best_defensive_rating = modified_defense;
1497  best_defensive = hex;
1498  }
1499 
1500  ++itors.first;
1501  }
1502 
1503  // If the unit is in range of its leader, it should
1504  // never retreat -- it has to defend the leader instead.
1505  if(can_reach_leader) {
1506  continue;
1507  }
1508 
1509  if(!best_pos.valid()) {
1510  best_pos = best_defensive;
1511  }
1512 
1513  if(best_pos.valid()) {
1514  move_ = check_move_action(i->get_location(), best_pos, true);
1515  if (move_->is_ok()) {
1516  return get_score();
1517  }
1518  }
1519  }
1520  }
1521  }
1522 
1523  return BAD_SCORE;
1524 }
1525 
1527 {
1528  move_->execute();
1529  if (!move_->is_ok()){
1530  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
1531  }
1532 }
1533 
1534 
1535 
1536 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)
1537 {
1538  const move_map &enemy_dstsrc = get_enemy_dstsrc();
1539 
1540  if(caution <= 0.0) {
1541  return false;
1542  }
1543 
1544  double optimal_terrain = best_defensive_position(un->get_location(), dstsrc,
1545  srcdst, enemy_dstsrc).chance_to_hit/100.0;
1546  const double proposed_terrain =
1547  un->defense_modifier(resources::gameboard->map().get_terrain(loc)) / 100.0;
1548 
1549  // The 'exposure' is the additional % chance to hit
1550  // this unit receives from being on a sub-optimal defensive terrain.
1551  const double exposure = proposed_terrain - optimal_terrain;
1552 
1553  const double our_power = power_projection(loc,dstsrc);
1554  const double their_power = power_projection(loc,enemy_dstsrc);
1555  return caution*their_power*(1.0+exposure) > our_power;
1556 }
1557 
1558 
1559 //==============================================================
1560 
1562  : candidate_action(context,cfg)
1563 {
1564 }
1565 
1566 
1568 {
1569 }
1570 
1572 {
1573  ERR_AI_TESTING_AI_DEFAULT << get_name() << ": evaluate - not yet implemented" << std::endl;
1574  return BAD_SCORE;
1575 }
1576 
1577 
1578 
1580 {
1581  ERR_AI_TESTING_AI_DEFAULT << get_name() << ": execute - not yet implemented" << std::endl;
1582 }
1583 
1584 //==============================================================
1585 
1587  :candidate_action(context, cfg)
1588 {
1589 }
1590 
1592 {
1593 }
1594 
1596 {
1598  return BAD_SCORE;
1599  }
1600  bool allied_leaders_available = false;
1601  for(team &tmp_team : *resources::teams) {
1602  if(!current_team().is_enemy(tmp_team.side())){
1603  std::vector<unit_map::unit_iterator> allied_leaders = resources::units->find_leaders(get_side());
1604  if (!allied_leaders.empty()){
1605  allied_leaders_available = true;
1606  break;
1607  }
1608  }
1609  }
1610  if(allied_leaders_available){
1611  return get_score();
1612  }
1613  return BAD_SCORE;
1614 }
1615 
1617 {
1618  //get all AI leaders
1619  std::vector<unit_map::unit_iterator> ai_leaders = resources::units->find_leaders(get_side());
1620 
1621  //calculate all possible moves (AI + allies)
1622  typedef std::map<map_location, pathfind::paths> path_map;
1623  path_map possible_moves;
1624  move_map friends_srcdst, friends_dstsrc;
1625  calculate_moves(*resources::units, possible_moves, friends_srcdst, friends_dstsrc, false, true);
1626 
1627  //check for each ai leader if he should move away from his keep
1628  for (unit_map::unit_iterator &ai_leader : ai_leaders) {
1629  //only if leader is on a keep
1630  const map_location &keep = ai_leader->get_location();
1631  if ( !resources::gameboard->map().is_keep(keep) ) {
1632  continue;
1633  }
1634  map_location recruit_loc = pathfind::find_vacant_castle(*ai_leader);
1635  if(!resources::gameboard->map().on_board(recruit_loc)){
1636  continue;
1637  }
1638  bool friend_can_reach_keep = false;
1639 
1640  //for each leader, check if he's allied and can reach our keep
1641  for(path_map::const_iterator i = possible_moves.begin(); i != possible_moves.end(); ++i){
1643  team &leader_team = (*resources::teams)[itor->side() - 1];
1644  if(itor != resources::units->end() && itor->can_recruit() && itor->side() != get_side() && (leader_team.total_income() + leader_team.gold() > leader_team.minimum_recruit_price())){
1645  pathfind::paths::dest_vect::const_iterator tokeep = i->second.destinations.find(keep);
1646  if(tokeep != i->second.destinations.end()){
1647  friend_can_reach_keep = true;
1648  break;
1649  }
1650  }
1651  }
1652  //if there's no allied leader who can reach the keep, check next ai leader
1653  if(friend_can_reach_keep){
1654  //determine the best place the ai leader can move to
1655  map_location best_move;
1656  int defense_modifier = 100;
1657  for(pathfind::paths::dest_vect::const_iterator i = possible_moves[keep].destinations.begin()
1658  ; i != possible_moves[keep].destinations.end()
1659  ; ++i){
1660 
1661  //calculate_moves() above uses max. moves -> need to check movement_left of leader here
1662  if(distance_between(i->curr, keep) <= 3
1663  && static_cast<int>(distance_between(i->curr, keep)) <= ai_leader->movement_left()){
1664 
1665  int tmp_def_mod = ai_leader->defense_modifier(resources::gameboard->map().get_terrain(i->curr));
1666  if(tmp_def_mod < defense_modifier){
1667  defense_modifier = tmp_def_mod;
1668  best_move = i->curr;
1669  }
1670  }
1671  }
1672  //only move if there's a place with a good defense
1673  if(defense_modifier < 100){
1674  move_result_ptr move = check_move_action(keep, best_move, true);
1675  if(move->is_ok()){
1676  move->execute();
1677  if (!move->is_ok()){
1678  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
1679  }else{
1680  ai_leader->set_goto(keep);
1681  }
1682  }else{
1683  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok" << std::endl;
1684  }
1685  }
1686  }
1687  ai_leader->remove_movement_ai();
1688  }
1689  for(unit_map::unit_iterator &leader : ai_leaders) {
1690  leader->remove_movement_ai();
1691  }
1692  //ERR_AI_TESTING_AI_DEFAULT << get_name() << ": evaluate - not yet implemented" << std::endl;
1693 }
1694 
1695 
1696 //==============================================================
1697 
1698 
1699 } //end of namespace testing_ai_default
1700 
1701 } //end of namespace ai
virtual side_number get_side() const
Get the side number.
Definition: contexts.hpp:480
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:220
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:336
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator *calc, const size_t width, const size_t height, const teleport_map *teleports, bool border)
std::vector< unit_iterator > find_leaders(int side)
Definition: map.cpp:319
unit_iterator end()
Definition: map.hpp:311
const map_location & get_location() const
Definition: unit.hpp:286
virtual config get_leader_goal() const
Definition: contexts.hpp:770
move_result_ptr move_
Definition: ca.hpp:52
std::vector< std::pair< map_location, map_location > > tmoves
Definition: ca.hpp:141
int max_hitpoints() const
Definition: unit.hpp:169
bool get_state(const std::string &state) const
Definition: unit.cpp:1289
std::string id_
Definition: formula.cpp:636
Definition: unit.hpp:95
bool dont_log(log_domain const &domain) const
Definition: log.hpp:122
virtual double get_caution() const
Definition: contexts.hpp:697
void dispatch(treachmap &reachmap, tmoves &moves)
Dispatches all units to their best location.
Definition: ca.cpp:768
map_location find_vacant_castle(const unit &leader)
Wrapper for find_vacant_tile() when looking for a vacant castle tile near a leader.
Definition: pathfind.cpp:121
goto_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:54
int movement_cost(const t_translation::t_terrain &terrain) const
Definition: unit.hpp:308
size_t count(const map_location &loc) const
Definition: map.hpp:306
map_location best_leader_loc_
The best possible location for our leader if it can't reach a village.
Definition: ca.hpp:132
unit_iterator find_leader(int side)
Definition: map.cpp:297
int hitpoints() const
Definition: unit.hpp:168
variant map_
Definition: formula.cpp:306
void dump_reachmap(treachmap &reachmap)
Shows which villages every unit can reach (debug function).
Definition: ca.cpp:1290
Managing the AI-Game interaction - AI actions and their results.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.hpp:274
leader_shares_keep_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:1586
virtual std::string get_name() const
Get the name of the candidate action (useful for debug purposes)
Definition: rca.hpp:93
bool is_village(const map_location &loc) const
Definition: map.cpp:68
std::map< map_location, pathfind::paths > moves_map
The standard way in which a map of possible movement routes to location is recorded.
Definition: game_info.hpp:50
double get_score() const
Get the usual score of the candidate action without re-evaluation.
Definition: rca.cpp:72
virtual void calculate_moves(const unit_map &units, std::map< map_location, pathfind::paths > &possible_moves, move_map &srcdst, move_map &dstsrc, bool enemy, bool assume_full_movement=false, const terrain_filter *remove_destinations=nullptr, bool see_all=false) const
Definition: contexts.hpp:596
map_location keep_loc_
Location of the keep the closest to our leader.
Definition: ca.hpp:126
bool dispatch_village_simple(treachmap &reachmap, tmoves &moves, size_t &village_count)
Definition: ca.cpp:873
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:161
map_location target
Definition: contexts.hpp:97
dest_vect destinations
Definition: pathfind.hpp:100
unit_iterator begin()
Definition: map.hpp:308
Composite AI stages.
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:1327
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:372
AI Support engine - creating specific ai components from config.
int side() const
Definition: unit.hpp:201
GLboolean GLboolean g
Definition: glew.h:7319
virtual const map_location & nearest_keep(const map_location &loc) const
Definition: contexts.hpp:946
bool get_ability_bool(const std::string &tag_name, const map_location &loc) const
Returns true if the unit is currently under effect by an ability with this given TAG NAME...
Definition: abilities.cpp:129
-file sdl_utils.hpp
bool empty() const
Definition: config.cpp:1105
move_leader_to_keep_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:361
retreat_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:1394
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:526
#define ERR_AI_TESTING_AI_DEFAULT
Definition: ca.cpp:45
virtual const attacks_vector & get_attacks() const
Definition: contexts.hpp:678
Default AI (Testing)
bool remove_village(treachmap &reachmap, tmoves &moves, const map_location &village)
Removes a village for all units, returns true if anything is deleted.
Definition: ca.cpp:925
int defense_modifier(const t_translation::t_terrain &terrain) const
Definition: unit.cpp:1520
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:1616
game_data * gamedata
Definition: resources.cpp:22
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:1403
virtual double get_leader_aggression() const
Definition: contexts.hpp:763
GLdouble l
Definition: glew.h:6966
virtual double power_projection(const map_location &loc, const move_map &dstsrc) const
Function which finds how much 'power' a side can attack a certain location with.
Definition: contexts.hpp:806
GLuint GLenum GLenum transform
Definition: glew.h:11418
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:47
bool contains(const map_location &) const
Definition: pathfind.cpp:510
const config & options()
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:50
static lg::log_domain log_ai_testing_ai_default("ai/ca/testing_ai_default")
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:57
virtual double get_aggression() const
Definition: contexts.hpp:641
virtual const moves_map & get_possible_moves() const
Definition: contexts.hpp:800
GLuint GLuint end
Definition: glew.h:1221
GLuint64EXT * result
Definition: glew.h:10727
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:258
std::vector< map_location > steps
Definition: pathfind.hpp:135
std::vector< team > * teams
Definition: resources.cpp:29
bool should_retreat(const map_location &loc, const unit_map::const_iterator &un, const move_map &srcdst, const move_map &dstsrc, double caution)
Definition: ca.cpp:1536
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:502
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:132
bool valid() const
Definition: location.hpp:69
map_location curr
Definition: pathfind.hpp:88
size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.hpp:357
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:1571
const GLdouble * v
Definition: glew.h:1359
GLenum GLenum dst
Definition: glew.h:2392
int w() const
Effective map width.
Definition: map.hpp:105
game_board * gameboard
Definition: resources.cpp:20
virtual move_result_ptr check_move_action(const map_location &from, const map_location &to, bool remove_movement=true, bool unreach_is_ok=false)
Definition: contexts.hpp:559
Encapsulates the map of the game.
Definition: map.hpp:37
bool can_recruit() const
Definition: unit.hpp:207
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:1595
virtual bool get_leader_ignores_keep() const
Definition: contexts.hpp:776
leader_control_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:1561
Managing the AIs lifecycle - headers.
virtual const move_map & get_srcdst() const
Definition: contexts.hpp:854
static void modify_active_ai_for_side(ai::side_number side, const config &cfg)
Modifies AI parameters for active AI of the given side.
Definition: manager.cpp:722
virtual bool get_passive_leader_shares_keep() const
Definition: contexts.hpp:794
virtual const map_location & suitable_keep(const map_location &leader_location, const pathfind::paths &leader_paths)
get most suitable keep for leader - nearest free that can be reached in 1 turn, if none - return near...
Definition: contexts.hpp:983
virtual void calculate_possible_moves(std::map< map_location, pathfind::paths > &possible_moves, move_map &srcdst, move_map &dstsrc, bool enemy, bool assume_full_movement=false, const terrain_filter *remove_destinations=nullptr) const
Definition: contexts.hpp:588
logger & debug()
Definition: log.cpp:97
static const map_location & null_location()
Definition: location.hpp:195
GLuint GLuint GLsizei count
Definition: glew.h:1221
virtual const move_map & get_enemy_dstsrc() const
Definition: contexts.hpp:709
int move_cost
Movement cost for reaching the end of the route.
Definition: pathfind.hpp:137
#define DBG_AI_TESTING_AI_DEFAULT
Definition: ca.cpp:42
Encapsulates the map of the game.
Definition: location.hpp:38
map_location leader_loc_
Locaton of our leader.
Definition: ca.hpp:129
void dispatch_complex(treachmap &reachmap, tmoves &moves, const size_t village_count)
Dispatches the units to a village after the simple dispatching failed.
Definition: ca.cpp:958
get_healing_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:1318
bool debug_
debug log level for AI enabled?
Definition: ca.hpp:135
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
bool dispatch_unit_simple(treachmap &reachmap, tmoves &moves)
Dispatches all units who can reach one village.
Definition: ca.cpp:828
int h() const
Effective map height.
Definition: map.hpp:108
GLuint GLenum matrix
Definition: glew.h:11418
virtual bool get_passive_leader() const
Definition: contexts.hpp:788
std::vector< std::pair< map_location, map_location > > movements
Definition: contexts.hpp:98
#define LOG_AI_TESTING_AI_DEFAULT
Definition: ca.cpp:43
size_t i
Definition: function.cpp:1057
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:538
defensive_position const & best_defensive_position(const map_location &unit, const move_map &dstsrc, const move_map &srcdst, const move_map &enemy_dstsrc) const
Definition: contexts.hpp:622
virtual move_result_ptr execute_move_action(const map_location &from, const map_location &to, bool remove_movement=true, bool unreach_is_ok=false)
Definition: contexts.hpp:1033
std::map< map_location, std::vector< map_location > > treachmap
Definition: ca.hpp:138
boost::shared_ptr< std::vector< unit_const_ptr > > units_
Definition: dialogs.cpp:100
void get_villages(const move_map &dstsrc, const move_map &enemy_dstsrc, unit_map::const_iterator &leader)
Definition: ca.cpp:589
virtual const team & current_team() const
Definition: contexts.hpp:539
virtual const gamemap & map() const
Definition: game_board.hpp:98
t_translation::t_terrain get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:341
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:467
#define next(ls)
Definition: llex.cpp:27
void full_dispatch(treachmap &reachmap, tmoves &moves)
Dispatches all units to a village, every unit can reach every village.
Definition: ca.cpp:1280
treachmap::iterator remove_unit(treachmap &reachmap, tmoves &moves, treachmap::iterator unit)
Removes a unit which can't reach any village anymore.
Definition: ca.cpp:942
int gold() const
Definition: team.hpp:194
get_villages_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:512
GLclampd n
Definition: glew.h:5903
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:1579
#define g
Definition: glew.h:12730
bool find(E event, F functor)
Tests whether an event handler is available.
map_location prev
Definition: astarsearch.cpp:67
Standard logging facilities (interface).
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:71
GLint * first
Definition: glew.h:1496
static const double BAD_SCORE
Definition: rca.hpp:38
Container associating units to locations.
Definition: map.hpp:90
const std::string remove
remove directive
int total_income() const
Definition: team.hpp:201
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:64
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:1383
unit_iterator find(size_t id)
Definition: map.cpp:285
bool valid() const
Definition: map.hpp:229
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units)
Definition: teleport.cpp:233
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
virtual const std::vector< std::string > get_recruitment_pattern() const
Definition: contexts.hpp:836
combat_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:152
virtual attack_result_ptr check_attack_action(const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon)
Definition: contexts.hpp:554
virtual const terrain_filter & get_avoid() const
Definition: contexts.hpp:691
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:128
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:1526
#define WRN_AI_TESTING_AI_DEFAULT
Definition: ca.cpp:44
This module contains various pathfinding functions and utilities.
void remove_goal(const std::string &id)
Definition: ca.cpp:350
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
virtual const move_map & get_dstsrc() const
Definition: contexts.hpp:703
virtual stopunit_result_ptr check_stopunit_action(const map_location &unit_location, bool remove_movement=true, bool remove_attacks=false)
Definition: contexts.hpp:578
void find_villages(treachmap &reachmap, tmoves &moves, const std::multimap< map_location, map_location > &dstsrc, const std::multimap< map_location, map_location > &enemy_dstsrc)
Definition: ca.cpp:649
move_leader_to_goals_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:249
candidate action framework
attack_analysis best_analysis_
Definition: ca.hpp:68
int minimum_recruit_price() const
Definition: team.cpp:420