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