The Battle for Wesnoth  1.17.8+dev
ca.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2022
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";
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";
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...";
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;
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()) << ")";
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";
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";
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";
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";
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";
284  return BAD_SCORE;
285  }
286 
287  if (goal.empty()) {
288  LOG_AI_TESTING_AI_DEFAULT << get_name() << "Empty goal found";
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";
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";
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";
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();
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...";
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.";
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.";
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.";
662  }
663 
664  LOG_AI_TESTING_AI_DEFAULT << "Village assignment done: " << (SDL_GetTicks() - ticks)
665  << " ms, resulted in " << moves_.size() << " units being dispatched.";
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;
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.";
780 }
781 
783 {
784  DBG_AI_TESTING_AI_DEFAULT << "Starting simple dispatch.";
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.";
803  break;
804  } else {
805  DBG_AI_TESTING_AI_DEFAULT << "dispatch_unit_simple() couldn't dispatch more units.";
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.";
814  break;
815  } else {
816  DBG_AI_TESTING_AI_DEFAULT << "dispatch_village_simple() couldn't dispatch more units.";
817  }
818  }
819 
820  if(!reachmap.empty() && dispatched) {
821  DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " unit(s) left restarting simple dispatching.";
822 
823  dump_reachmap(reachmap);
824  }
825  }
826 
827  if(reachmap.empty()) {
828  DBG_AI_TESTING_AI_DEFAULT << "No units left after simple dispatcher.";
829  return;
830  }
831 
832  DBG_AI_TESTING_AI_DEFAULT << reachmap.size() << " units left for complex dispatch with "
833  << village_count << " villages left.";
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;
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];
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;
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_;
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.";
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  STREAMING_LOG << "Reach matrix:\n\nvillage";
1036  std::size_t u, v;
1037  for(v = 0; v < village_count; ++v) {
1038  STREAMING_LOG << '\t' << villages[v];
1039  }
1040  STREAMING_LOG << "\ttotal\nunit\n";
1041 
1042  // Print data
1043  for(u = 0; u < unit_count; ++u) {
1044  STREAMING_LOG << units[u];
1045 
1046  for(v = 0; v < village_count; ++v) {
1047  STREAMING_LOG << '\t' << matrix[u][v];
1048  }
1049  STREAMING_LOG << "\t" << villages_per_unit[u] << '\n';
1050  }
1051 
1052  // Print footer
1053  STREAMING_LOG << "total";
1054  for(v = 0; v < village_count; ++v) {
1055  STREAMING_LOG << '\t' << units_per_village[v];
1056  }
1057  STREAMING_LOG << '\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";
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;
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;
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.";
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.";
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;";
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";
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";
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];
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  STREAMING_LOG << "Reachlist for unit at " << itor->first;
1303 
1304  if(itor->second.empty()) {
1305  STREAMING_LOG << "\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  STREAMING_LOG << '\t' << *v_itor;
1313  }
1314  STREAMING_LOG << '\n';
1315  }
1316 }
1317 
1318 //==============================================================
1319 
1321  : candidate_action(context,cfg),move_()
1322 {
1323 }
1324 
1326 {
1327 }
1328 
1330 {
1331  // Find units in need of healing.
1332  unit_map &units_ = resources::gameboard->units();
1333  unit_map::iterator u_it = units_.begin();
1334  for(; u_it != units_.end(); ++u_it) {
1335  unit &u = *u_it;
1336 
1337  if(u.can_recruit() && is_passive_leader(u.id())){
1338  continue;
1339  }
1340 
1341  // If the unit is on our side, has lost as many or more than
1342  // 1/2 round worth of healing, and doesn't regenerate itself,
1343  // then try to find a vacant village for it to rest in.
1344  if(u.side() == get_side() &&
1347  !u.get_ability_bool("regenerate") && is_allowed_unit(*u_it))
1348  {
1349  // Look for the village which is the least vulnerable to enemy attack.
1350  typedef std::multimap<map_location,map_location>::const_iterator Itor;
1351  std::pair<Itor,Itor> it = get_srcdst().equal_range(u_it->get_location());
1352  double best_vulnerability = 100000.0;
1353  // Make leader units more unlikely to move to vulnerable villages
1354  const double leader_penalty = (u.can_recruit()?2.0:1.0);
1355  Itor best_loc = it.second;
1356  while(it.first != it.second) {
1357  const map_location& dst = it.first->second;
1358  if (resources::gameboard->map().gives_healing(dst) && (units_.find(dst) == units_.end() || dst == u_it->get_location())) {
1359  const double vuln = power_projection(dst, get_enemy_dstsrc());
1360  DBG_AI_TESTING_AI_DEFAULT << "found village with vulnerability: " << vuln;
1361  if(vuln < best_vulnerability) {
1362  best_vulnerability = vuln;
1363  best_loc = it.first;
1364  DBG_AI_TESTING_AI_DEFAULT << "chose village " << dst;
1365  }
1366  }
1367 
1368  ++it.first;
1369  }
1370 
1371  // If we have found an eligible village,
1372  // and we can move there without expecting to get whacked next turn:
1373  if(best_loc != it.second && best_vulnerability*leader_penalty < u.hitpoints()) {
1374  move_ = check_move_action(best_loc->first,best_loc->second,true);
1375  if (move_->is_ok()) {
1376  return get_score();
1377  }
1378  }
1379  }
1380  }
1381 
1382  return BAD_SCORE;
1383 }
1384 
1386 {
1387  LOG_AI_TESTING_AI_DEFAULT << "moving unit to village for healing...";
1388  move_->execute();
1389  if (!move_->is_ok()){
1390  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok";
1391  }
1392 }
1393 
1394 //==============================================================
1395 
1397  : candidate_action(context,cfg), move_()
1398 {
1399 }
1400 
1402 {
1403 }
1404 
1406 {
1407 
1408  // Get versions of the move map that assume that all units are at full movement
1409  const unit_map& units_ = resources::gameboard->units();
1410 
1411  //unit_map::const_iterator leader = units_.find_leader(get_side());
1412  std::vector<unit_map::const_iterator> leaders = units_.find_leaders(get_side());
1413  std::map<map_location,pathfind::paths> dummy_possible_moves;
1414 
1415  move_map fullmove_srcdst;
1416  move_map fullmove_dstsrc;
1417  calculate_possible_moves(dummy_possible_moves, fullmove_srcdst, fullmove_dstsrc,
1418  false, true, &get_avoid());
1419 
1420  std::vector<map_location> leaders_adj_v;
1421  for (unit_map::const_iterator leader : leaders) {
1422  for(const map_location& loc : get_adjacent_tiles(leader->get_location())) {
1423  bool found = false;
1424  for (map_location &new_loc : leaders_adj_v) {
1425  if(new_loc == loc){
1426  found = true;
1427  break;
1428  }
1429  }
1430  if(!found){
1431  leaders_adj_v.push_back(loc);
1432  }
1433  }
1434  }
1435  //leader_adj_count = leaders_adj_v.size();
1436 
1437  for(unit_map::const_iterator i = units_.begin(); i != units_.end(); ++i) {
1438  if (i->side() == get_side() &&
1439  i->movement_left() == i->total_movement() &&
1440  //leaders.find(*i) == leaders.end() && //unit_map::const_iterator(i) != leader &&
1441  std::find(leaders.begin(), leaders.end(), i) == leaders.end() &&
1442  !i->incapacitated() && is_allowed_unit(*i))
1443  {
1444  // This unit still has movement left, and is a candidate to retreat.
1445  // We see the amount of power of each side on the situation,
1446  // and decide whether it should retreat.
1447  if(should_retreat(i->get_location(), i, fullmove_srcdst, fullmove_dstsrc, get_caution())) {
1448 
1449  bool can_reach_leader = false;
1450 
1451  // Time to retreat. Look for the place where the power balance
1452  // is most in our favor.
1453  // If we can't find anywhere where we like the power balance,
1454  // just try to get to the best defensive hex.
1455  typedef move_map::const_iterator Itor;
1456  std::pair<Itor,Itor> itors = get_srcdst().equal_range(i->get_location());
1457  map_location best_pos, best_defensive(i->get_location());
1458 
1459  double best_rating = -1000.0;
1460  int best_defensive_rating = i->defense_modifier(resources::gameboard->map().get_terrain(i->get_location()))
1461  - (resources::gameboard->map().is_village(i->get_location()) ? 10 : 0);
1462  while(itors.first != itors.second) {
1463 
1464  //if(leader != units_.end() && std::count(leader_adj,
1465  // leader_adj + 6, itors.first->second)) {
1466  if(std::find(leaders_adj_v.begin(), leaders_adj_v.end(), itors.first->second) != leaders_adj_v.end()){
1467 
1468  can_reach_leader = true;
1469  break;
1470  }
1471 
1472  // We rate the power balance of a hex based on our power projection
1473  // compared to theirs, multiplying their power projection by their
1474  // chance to hit us on the hex we're planning to flee to.
1475  const map_location& hex = itors.first->second;
1476  const int defense = i->defense_modifier(resources::gameboard->map().get_terrain(hex));
1477  const double our_power = power_projection(hex,get_dstsrc());
1478  const double their_power = power_projection(hex,get_enemy_dstsrc()) * static_cast<double>(defense)/100.0;
1479  const double rating = our_power - their_power;
1480  if(rating > best_rating) {
1481  best_pos = hex;
1482  best_rating = rating;
1483  }
1484 
1485  // Give a bonus for getting to a village.
1486  const int modified_defense = defense - (resources::gameboard->map().is_village(hex) ? 10 : 0);
1487 
1488  if(modified_defense < best_defensive_rating) {
1489  best_defensive_rating = modified_defense;
1490  best_defensive = hex;
1491  }
1492 
1493  ++itors.first;
1494  }
1495 
1496  // If the unit is in range of its leader, it should
1497  // never retreat -- it has to defend the leader instead.
1498  if(can_reach_leader) {
1499  continue;
1500  }
1501 
1502  if(!best_pos.valid()) {
1503  best_pos = best_defensive;
1504  }
1505 
1506  if(best_pos.valid()) {
1507  move_ = check_move_action(i->get_location(), best_pos, true);
1508  if (move_->is_ok()) {
1509  return get_score();
1510  }
1511  }
1512  }
1513  }
1514  }
1515 
1516  return BAD_SCORE;
1517 }
1518 
1520 {
1521  move_->execute();
1522  if (!move_->is_ok()){
1523  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok";
1524  }
1525 }
1526 
1527 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)
1528 {
1529  const move_map &enemy_dstsrc = get_enemy_dstsrc();
1530 
1531  if(caution <= 0.0) {
1532  return false;
1533  }
1534 
1535  double optimal_terrain = best_defensive_position(un->get_location(), dstsrc,
1536  srcdst, enemy_dstsrc).chance_to_hit/100.0;
1537  const double proposed_terrain =
1538  un->defense_modifier(resources::gameboard->map().get_terrain(loc)) / 100.0;
1539 
1540  // The 'exposure' is the additional % chance to hit
1541  // this unit receives from being on a sub-optimal defensive terrain.
1542  const double exposure = proposed_terrain - optimal_terrain;
1543 
1544  const double our_power = power_projection(loc,dstsrc);
1545  const double their_power = power_projection(loc,enemy_dstsrc);
1546  return caution*their_power*(1.0+exposure) > our_power;
1547 }
1548 
1549 //==============================================================
1550 
1552  : candidate_action(context,cfg)
1553 {
1554 }
1555 
1557 {
1558 }
1559 
1561 {
1562  ERR_AI_TESTING_AI_DEFAULT << get_name() << ": evaluate - not yet implemented";
1563  return BAD_SCORE;
1564 }
1565 
1567 {
1568  ERR_AI_TESTING_AI_DEFAULT << get_name() << ": execute - not yet implemented";
1569 }
1570 
1571 //==============================================================
1572 
1574  :candidate_action(context, cfg)
1575 {
1576 }
1577 
1579 {
1580 }
1581 
1583 {
1584  bool have_active_leader = false;
1585  std::vector<unit_map::unit_iterator> ai_leaders = resources::gameboard->units().find_leaders(get_side());
1586  for (unit_map::unit_iterator &ai_leader : ai_leaders) {
1587  if (!is_passive_leader(ai_leader->id()) || is_passive_keep_sharing_leader(ai_leader->id())) {
1588  have_active_leader = true;
1589  break;
1590  }
1591  }
1592  if(!have_active_leader) {
1593  return BAD_SCORE;
1594  }
1595 
1596  bool allied_leaders_available = false;
1597  for(team &tmp_team : resources::gameboard->teams()) {
1598  if(!current_team().is_enemy(tmp_team.side())){
1599  std::vector<unit_map::unit_iterator> allied_leaders = resources::gameboard->units().find_leaders(get_side());
1600  if (!allied_leaders.empty()){
1601  allied_leaders_available = true;
1602  break;
1603  }
1604  }
1605  }
1606  if(allied_leaders_available){
1607  return get_score();
1608  }
1609  return BAD_SCORE;
1610 }
1611 
1613 {
1614  //get all AI leaders
1615  std::vector<unit_map::unit_iterator> ai_leaders = resources::gameboard->units().find_leaders(get_side());
1616 
1617  //calculate all possible moves (AI + allies)
1618  typedef std::map<map_location, pathfind::paths> path_map;
1619  path_map possible_moves;
1620  move_map friends_srcdst, friends_dstsrc;
1621  calculate_moves(resources::gameboard->units(), possible_moves, friends_srcdst, friends_dstsrc, false, true);
1622 
1623  //check for each ai leader if he should move away from his keep
1624  for (unit_map::unit_iterator &ai_leader : ai_leaders) {
1625  if(!ai_leader.valid() || !is_allowed_unit(*ai_leader) || (is_passive_leader(ai_leader->id()) && !is_passive_keep_sharing_leader(ai_leader->id()))) {
1626  //This can happen if wml killed or moved a leader during a movement events of another leader
1627  continue;
1628  }
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){
1642  const unit_map::const_iterator itor = resources::gameboard->units().find(i->first);
1643  assert(itor.valid());
1644  team &leader_team = resources::gameboard->get_team(itor->side());
1645  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())){
1646  pathfind::paths::dest_vect::const_iterator tokeep = i->second.destinations.find(keep);
1647  if(tokeep != i->second.destinations.end()){
1648  friend_can_reach_keep = true;
1649  break;
1650  }
1651  }
1652  }
1653  //if there's no allied leader who can reach the keep, check next ai leader
1654  if(friend_can_reach_keep){
1655  //determine the best place the ai leader can move to
1656  map_location best_move;
1657  int defense_modifier = 100;
1658  for(pathfind::paths::dest_vect::const_iterator i = possible_moves[keep].destinations.begin()
1659  ; i != possible_moves[keep].destinations.end()
1660  ; ++i){
1661 
1662  //calculate_moves() above uses max. moves -> need to check movement_left of leader here
1663  if(distance_between(i->curr, keep) <= 3
1664  && static_cast<int>(distance_between(i->curr, keep)) <= ai_leader->movement_left()){
1665 
1666  int tmp_def_mod = ai_leader->defense_modifier(resources::gameboard->map().get_terrain(i->curr));
1667  if(tmp_def_mod < defense_modifier){
1668  defense_modifier = tmp_def_mod;
1669  best_move = i->curr;
1670  }
1671  }
1672  }
1673  //only move if there's a place with a good defense
1674  if(defense_modifier < 100){
1675  move_result_ptr move = check_move_action(keep, best_move, true);
1676  if(move->is_ok()){
1677  move->execute();
1678  if (!move->is_ok()){
1679  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok";
1680  }else{
1681  ai_leader->set_goto(keep);
1682  }
1683  // This is needed for sides with multiple leaders, in case a WML event does something
1684  // or to account for a leader having previously been moved by this CA execution
1685  possible_moves.clear();
1686  calculate_moves(resources::gameboard->units(), possible_moves, friends_srcdst, friends_dstsrc, false, true);
1687  }else{
1688  LOG_AI_TESTING_AI_DEFAULT << get_name() << "::execute not ok";
1689  }
1690  }
1691  }
1692  ai_leader->remove_movement_ai();
1693  }
1694  //ERR_AI_TESTING_AI_DEFAULT << get_name() << ": evaluate - not yet implemented";
1695 }
1696 
1697 //==============================================================
1698 
1699 } //end of namespace testing_ai_default
1700 
1701 } //end of namespace ai
void remove()
Removes a tip.
Definition: tooltip.cpp:111
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:472
std::vector< unit_iterator > find_leaders(int side)
Definition: map.cpp:347
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:583
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:86
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:113
virtual const map_location & nearest_keep(const map_location &loc) const override
Definition: contexts.hpp:828
This class represents a single unit of a specific type.
Definition: unit.hpp:120
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:1428
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:117
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:1316
virtual stopunit_result_ptr check_stopunit_action(const map_location &unit_location, bool remove_movement=true, bool remove_attacks=false) override
Definition: contexts.hpp:494
unit_iterator find_leader(int side)
Definition: map.cpp:319
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:489
leader_shares_keep_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:1573
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:723
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:74
virtual const gamemap & map() const override
Definition: game_board.hpp:103
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:851
Composite AI stages.
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:1329
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:1396
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:1612
game_data * gamedata
Definition: resources.cpp:23
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: ca.cpp:1405
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:1610
const config & options()
Definition: game.cpp:556
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:75
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:98
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:370
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:1527
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
#define STREAMING_LOG
Definition: log.hpp:257
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:537
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:1560
virtual double get_leader_aggression() const override
Definition: contexts.hpp:653
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:1582
bool is_enemy(int n) const
Definition: team.hpp:231
virtual bool is_passive_leader(const std::string &id) const override
Definition: contexts.hpp:773
virtual const move_map & get_enemy_dstsrc() const override
Definition: contexts.hpp:608
leader_control_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:1551
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:708
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:667
logger & debug()
Definition: log.cpp:177
bool dont_log(const log_domain &domain) const
Definition: log.hpp:165
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:301
get_healing_phase(rca_context &context, const config &cfg)
Definition: ca.cpp:1320
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:150
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:863
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:457
int max_hitpoints() const
The max number of hitpoints this unit can have.
Definition: unit.hpp:495
double g
Definition: astarsearch.cpp:65
std::vector< std::pair< map_location, map_location > > movements
Definition: contexts.hpp:75
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
Definition: unit.hpp:602
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:688
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:403
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:477
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:778
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
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:603
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:553
virtual void execute()
Execute the candidate action.
Definition: ca.cpp:1566
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:504
map_location prev
Definition: astarsearch.cpp:66
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1345
bool contains(const map_location &) const
Definition: pathfind.cpp:514
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:768
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:512
int side() const
The side this unit belongs to.
Definition: unit.hpp:333
virtual const terrain_filter & get_avoid() const override
Definition: contexts.hpp:593
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:1385
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:60
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:905
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:598
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:1519
#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:683
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
virtual config get_leader_goal() const override
Definition: contexts.hpp:658
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