The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ca_move_to_targets.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2016 by Yurii Chernyi <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Strategic movement routine, taken from default AI
18  */
19 
21 
22 #include "ai/composite/ai.hpp"
23 #include "ai/actions.hpp"
24 #include "game_board.hpp"
25 #include "log.hpp"
26 #include "map/map.hpp"
27 #include "resources.hpp"
28 #include "units/unit.hpp"
29 #include "terrain/filter.hpp"
30 #include "pathfind/pathfind.hpp"
31 #include "pathfind/teleport.hpp"
32 
33 #include <deque>
34 
35 namespace ai {
36 
37 namespace ai_default_rca {
38 
39 static lg::log_domain log_ai_testing_ca_move_to_targets("ai/ca/move_to_targets");
40 #define DBG_AI LOG_STREAM(debug, log_ai_testing_ca_move_to_targets)
41 #define LOG_AI LOG_STREAM(info, log_ai_testing_ca_move_to_targets)
42 #define WRN_AI LOG_STREAM(warn, log_ai_testing_ca_move_to_targets)
43 #define ERR_AI LOG_STREAM(err, log_ai_testing_ca_move_to_targets)
44 
45 
46 
48 {
49  move_cost_calculator(const unit& u, const gamemap& map,
50  const unit_map& units, const move_map& enemy_dstsrc)
51  : unit_(u), map_(map), units_(units),
52  enemy_dstsrc_(enemy_dstsrc),
53  max_moves_(u.total_movement()),
54  avoid_enemies_(u.usage() == "scout")
55  {}
56 
57  double cost(const map_location& loc, const double) const
58  {
60 
61  const double move_cost = unit_.movement_cost(terrain);
62 
63  if(move_cost > max_moves_) // impassable
64  return getNoPathValue();
65 
66  double res = move_cost;
67  if(avoid_enemies_){
68  res *= 1.0 + enemy_dstsrc_.count(loc);
69  }
70 
71  //if there is a unit (even a friendly one) on this tile, we increase the cost to
72  //try discourage going through units, to thwart the 'single file effect'
73  if (units_.count(loc))
74  res *= 4.0;
75 
76  return res;
77  }
78 
79 private:
80  const unit& unit_;
81  const gamemap& map_;
82  const unit_map& units_;
84  const int max_moves_;
85  const bool avoid_enemies_;
86 };
87 
88 
90 public:
92  :avoid_(context.get_avoid()), map_(resources::gameboard->map())
93  {
94  }
95 
96 bool operator()(const target &t){
97  if (!map_.on_board(t.loc)) {
98  DBG_AI << "removing target "<< t.loc << " due to it not on_board" << std::endl;
99  return true;
100  }
101 
102  if (t.value<=0) {
103  DBG_AI << "removing target "<< t.loc << " due to value<=0" << std::endl;
104  return true;
105  }
106 
107  if (avoid_.match(t.loc)) {
108  DBG_AI << "removing target "<< t.loc << " due to 'avoid' match" << std::endl;
109  return true;
110  }
111 
112  return false;
113 }
114 private:
116  const gamemap &map_;
117 
118 };
119 
121  : candidate_action(context,cfg)
122 {
123 }
124 
125 
127 {
128 }
129 
130 
132 {
133  return get_score();
134 }
135 
136 
138 {
140  LOG_AI << "finding targets...\n";
141  std::vector<target> targets;
142  for(;;) {
143  if(targets.empty()) {
144  targets = find_targets(get_enemy_dstsrc());
145  targets.insert(targets.end(),additional_targets().begin(),
146  additional_targets().end());
147  LOG_AI << "Found " << targets.size() << " targets\n";
148  if(targets.empty()) {
149  break;
150  }
151  }
152 
153  targets.erase( std::remove_if(targets.begin(),targets.end(),remove_wrong_targets(*this)), targets.end() );
154 
155  if(targets.empty()) {
156  break;
157  }
158 
159  LOG_AI << "choosing move with " << targets.size() << " targets\n";
160  std::pair<map_location,map_location> move = choose_move(targets, get_srcdst(),
162  LOG_AI << "choose_move ends with " << targets.size() << " targets\n";
163 
164  for(std::vector<target>::const_iterator ittg = targets.begin();
165  ittg != targets.end(); ++ittg) {
166  assert(resources::gameboard->map().on_board(ittg->loc));
167  }
168 
169  if(move.first.valid() == false || move.second.valid() == false) {
170  break;
171  }
172 
173  assert (resources::gameboard->map().on_board(move.first)
174  && resources::gameboard->map().on_board(move.second));
175 
176  LOG_AI << "move: " << move.first << " -> " << move.second << '\n';
177 
178  move_result_ptr move_ptr = execute_move_action(move.first,move.second,true);
179  if(!move_ptr->is_ok()) {
180  WRN_AI << "unexpected outcome of move"<<std::endl;
181  break;
182  }
183  }
184 }
185 
186 
187 
188 
189 
190 // structure storing the maximal possible rating of a target
194  double max_rating;
195 };
196 
197 // compare maximal possible rating of targets
198 // we can be smarter about the equal case, but keep old behavior for the moment
200  bool operator()(const rated_target& a, const rated_target& b) const {
201  return a.max_rating > b.max_rating;
202  }
203 };
204 
205 
207  const move_map& dstsrc, const move_map& enemy_dstsrc,
208  const pathfind::plain_route& rt)
209 {
210  double move_cost = rt.move_cost;
211 
212  if(move_cost > 0) {
213  // if this unit can move to that location this turn, it has a very very low cost
214  typedef std::multimap<map_location,map_location>::const_iterator multimapItor;
215  std::pair<multimapItor,multimapItor> locRange = dstsrc.equal_range(tg.loc);
216  while (locRange.first != locRange.second) {
217  if (locRange.first->second == u->get_location()) {
218  move_cost = 0;
219  break;
220  }
221  ++locRange.first;
222  }
223  }
224 
225  double rating = tg.value;
226 
227  if(rating == 0)
228  return rating; // all following operations are only multiplications of 0
229 
230  // far target have a lower rating
231  if(move_cost > 0) {
232  rating /= move_cost;
233  }
234 
235  //for 'support' targets, they are rated much higher if we can get there within two turns,
236  //otherwise they are worthless to go for at all.
237  if(tg.type == target::TYPE::SUPPORT) {
238  if (move_cost <= u->movement_left() * 2) {
239  rating *= 10.0;
240  } else {
241  rating = 0.0;
242  return rating;
243  }
244  }
245 
246  //scouts do not like encountering enemies on their paths
247  if (u->usage() == "scout") {
248  //scouts get a bonus for going after villages
249  if(tg.type == target::TYPE::VILLAGE) {
250  rating *= get_scout_village_targeting();
251  }
252 
253  std::set<map_location> enemies_guarding;
254  enemies_along_path(rt.steps,enemy_dstsrc,enemies_guarding);
255  // note that an empty route means no guardian and thus optimal rating
256 
257  if(enemies_guarding.size() > 1) {
258  rating /= enemies_guarding.size();
259  } else {
260  //scouts who can travel on their route without coming in range of many enemies
261  //get a massive bonus, so that they can be processed first, and avoid getting
262  //bogged down in lots of grouping
263  rating *= 100;
264  }
265  }
266 
267  return rating;
268 }
269 
270 
271 
272 std::pair<map_location,map_location> move_to_targets_phase::choose_move(std::vector<target>& targets, const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc)
273 {
275 
278  const gamemap &map_ = resources::gameboard->map();
279 
281 
282  //find the first eligible unit
283  for(u = units_.begin(); u != units_.end(); ++u) {
284  if (!(u->side() != get_side() || (u->can_recruit() && !get_leader_ignores_keep()) || u->movement_left() <= 0 || u->incapacitated())) {
285  break;
286  }
287  }
288 
289  if(u == units_.end()) {
290  LOG_AI << "no eligible units found\n";
291  return std::pair<map_location,map_location>();
292  }
293 
294  //guardian units stay put
295  if (u->get_state("guardian")) {
296  LOG_AI << u->type_id() << " is guardian, staying still\n";
297  return std::make_pair(u->get_location(), u->get_location());
298  }
299 
300  const pathfind::plain_route dummy_route;
301  assert(dummy_route.steps.empty() && dummy_route.move_cost == 0);
302 
303  // We will sort all targets by a quick maximal possible rating,
304  // so we will be able to start real work by the most promising ones
305  // and if its real value is better than other maximal values
306  // then we can skip them.
307 
308  std::vector<rated_target> rated_targets;
309  for(std::vector<target>::iterator tg = targets.begin(); tg != targets.end(); ++tg) {
310  // passing a dummy route to have the maximal rating
311  double max_rating = rate_target(*tg, u, dstsrc, enemy_dstsrc, dummy_route);
312  rated_targets.push_back( rated_target(tg, max_rating) );
313  }
314 
315  //use stable_sort for the moment to preserve old AI behavior
316  std::stable_sort(rated_targets.begin(), rated_targets.end(), rated_target_comparer());
317 
318  const move_cost_calculator cost_calc(*u, map_, units_, enemy_dstsrc);
319 
320  pathfind::plain_route best_route;
321  unit_map::iterator best = units_.end();
322  double best_rating = -1.0;
323 
324  std::vector<rated_target>::iterator best_rated_target = rated_targets.end();
325 
326  std::vector<rated_target>::iterator rated_tg = rated_targets.begin();
327 
328  for(; rated_tg != rated_targets.end(); ++rated_tg) {
329  const target& tg = *(rated_tg->tg);
330 
331  LOG_AI << "Considering target at: " << tg.loc <<"\n";
332  assert(map_.on_board(tg.loc));
333 
335 
336  // locStopValue controls how quickly we give up on the A* search, due
337  // to it seeming futile. Be very cautious about changing this value,
338  // as it can cause the AI to give up on searches and just do nothing.
339  const double locStopValue = 500.0;
341  pathfind::plain_route real_route = a_star_search(u->get_location(), tg.loc, locStopValue, &cost_calc, map_.w(), map_.h(), &allowed_teleports);
342 
343  if(real_route.steps.empty()) {
344  LOG_AI << "Can't reach target: " << locStopValue << " = " << tg.value << "/" << best_rating << "\n";
345  continue;
346  }
347 
348  double real_rating = rate_target(tg, u, dstsrc, enemy_dstsrc, real_route);
349 
350  LOG_AI << tg.value << "/" << real_route.move_cost << " = " << real_rating << "\n";
351 
352  if(real_rating > best_rating){
353  best_rating = real_rating;
354  best_rated_target = rated_tg;
355  best_route = real_route;
356  best = u;
357  //prevent divivion by zero
358  //FIXME: stupid, should fix it at the division
359  if(best_rating == 0)
360  best_rating = 0.000000001;
361 
362  // if already higher than the maximal values of the next ratings
363  // (which are sorted, so only need to check the next one)
364  // then we have found the best target.
365  if(rated_tg+1 != rated_targets.end() && best_rating >= (rated_tg+1)->max_rating)
366  break;
367  }
368  }
369 
370  LOG_AI << "choose target...\n";
371 
372  if(best_rated_target == rated_targets.end()) {
373  LOG_AI << "no eligible targets found for unit at " << u->get_location() << std::endl;
374  return std::make_pair(u->get_location(), u->get_location());
375  }
376 
377  assert(best_rating >= 0);
378  std::vector<target>::iterator best_target = best_rated_target->tg;
379 
380  //if we have the 'simple_targeting' flag set, then we don't
381  //see if any other units can put a better bid forward for this
382  //target
383  bool simple_targeting = get_simple_targeting();
384 
385  if(simple_targeting == false) {
386  LOG_AI << "complex targeting...\n";
387  //now see if any other unit can put a better bid forward
388  for(++u; u != units_.end(); ++u) {
389  if (u->side() != get_side() || (u->can_recruit() && !get_leader_ignores_keep()) ||
390  u->movement_left() <= 0 || u->get_state("guardian") ||
391  u->incapacitated())
392  {
393  continue;
394  }
395 
397 
398  const move_cost_calculator calc(*u, map_, units_, enemy_dstsrc);
399 
400  ///@todo 1.9: lower this value for perf,
401  // but best_rating is too big for scout and support
402  // which give a too small locStopValue
403  // so keep costy A* for the moment.
404  //const double locStopValue = std::min(best_target->value / best_rating, (double) 100.0);
405 
406  const double locStopValue = 500.0;
408  pathfind::plain_route cur_route = pathfind::a_star_search(u->get_location(), best_target->loc, locStopValue, &calc, map_.w(), map_.h(), &allowed_teleports);
409 
410  if(cur_route.steps.empty()) {
411  continue;
412  }
413 
414  double rating = rate_target(*best_target, u, dstsrc, enemy_dstsrc, cur_route);
415 
416  if(best == units_.end() || rating > best_rating) {
417  best_rating = rating;
418  best = u;
419  best_route = cur_route;
420  }
421  }
422 
423  LOG_AI << "done complex targeting...\n";
424  } else {
425  u = units_.end();
426  }
427 
428  LOG_AI << "best unit: " << best->get_location() << '\n';
429 
430  assert(best_target != targets.end());
431 
432  //if our target is a position to support, then we
433  //see if we can move to a position in support of this target
434  if(best_target->type == target::TYPE::SUPPORT) {
435  LOG_AI << "support...\n";
436 
437  std::vector<map_location> locs;
438  access_points(srcdst, best->get_location(), best_target->loc, locs);
439 
440  if(locs.empty() == false) {
441  LOG_AI << "supporting unit at " << best_target->loc.x + 1 << "," << best_target->loc.y + 1 << "\n";
442  map_location best_loc;
443  int best_defense = 0;
444  double best_vulnerability = 0.0;
445 
446  for(std::vector<map_location>::const_iterator i = locs.begin(); i != locs.end(); ++i) {
447  const int defense = best->defense_modifier(map_.get_terrain(*i));
448  //FIXME: suokko multiplied by 10 * get_caution(). ?
449  const double vulnerability = power_projection(*i,enemy_dstsrc);
450 
451  if(best_loc.valid() == false || defense < best_defense || (defense == best_defense && vulnerability < best_vulnerability)) {
452  best_loc = *i;
453  best_defense = defense;
454  best_vulnerability = vulnerability;
455  }
456  }
457 
458  LOG_AI << "returning support...\n";
459  return std::make_pair(best->get_location(), best_loc);
460  }
461  }
462 
463  std::map<map_location,pathfind::paths> dummy_possible_moves;
464  move_map fullmove_srcdst;
465  move_map fullmove_dstsrc;
466  calculate_possible_moves(dummy_possible_moves,fullmove_srcdst,fullmove_dstsrc,false,true);
467 
468  bool dangerous = false;
469 
470  if(get_grouping() != "no") {
471  LOG_AI << "grouping...\n";
472  const unit_map::const_iterator unit_at_target = units_.find(best_target->loc);
473  int movement = best->movement_left();
474 
475  const bool defensive_grouping = get_grouping() == "defensive";
476 
477  //we stop and consider whether the route to this
478  //target is dangerous, and whether we need to group some units to move in unison toward the target
479  //if any point along the path is too dangerous for our single unit, then we hold back
480  for(std::vector<map_location>::const_iterator i = best_route.steps.begin(); i != best_route.steps.end() && movement > 0; ++i) {
481 
482  //FIXME: suokko multiplied by 10 * get_caution(). ?
483  const double threat = power_projection(*i,enemy_dstsrc);
484  //FIXME: sukko doubled the power-projection them in the second test. ?
485  if ((threat >= best->hitpoints() && threat > power_projection(*i,fullmove_dstsrc)) ||
486  (i+1 >= best_route.steps.end()-1 && unit_at_target != units_.end() && current_team().is_enemy(unit_at_target->side()))) {
487  dangerous = true;
488  break;
489  }
490 
491  if(!defensive_grouping) {
492  movement -= best->movement_cost(map_.get_terrain(*i));
493  }
494  }
495 
496  LOG_AI << "done grouping...\n";
497  }
498 
499  if(dangerous) {
500  LOG_AI << "dangerous path\n";
501  std::set<map_location> group, enemies;
502  const map_location dst = form_group(best_route.steps,dstsrc,group);
503  enemies_along_path(best_route.steps,enemy_dstsrc,enemies);
504 
505  const double our_strength = compare_groups(group,enemies,best_route.steps);
506 
507  if(our_strength > 0.5 + get_caution()) {
508  LOG_AI << "moving group\n";
509  const bool res = move_group(dst,best_route.steps,group);
510  if(res) {
511  return std::pair<map_location,map_location>(map_location(1,1),map_location());
512  } else {
513  LOG_AI << "group didn't move " << group.size() << "\n";
514 
515  //the group didn't move, so end the first unit in the group's turn, to prevent an infinite loop
516  return std::make_pair(best->get_location(), best->get_location());
517 
518  }
519  } else {
520  LOG_AI << "massing to attack " << best_target->loc.x + 1 << "," << best_target->loc.y + 1
521  << " " << our_strength << "\n";
522 
523  const double value = best_target->value;
524  const map_location target_loc = best_target->loc;
525  const map_location loc = best->get_location();
526  const unit& un = *best;
527 
528  targets.erase(best_target);
529 
530  //find the best location to mass units at for an attack on the enemies
531  map_location best_loc;
532  double best_threat = 0.0;
533  int best_distance = 0;
534 
535  const double max_acceptable_threat = un.hitpoints()/4;
536 
537  std::set<map_location> mass_locations;
538 
539  const std::pair<move_map::const_iterator,move_map::const_iterator> itors = srcdst.equal_range(loc);
540  for(move_map::const_iterator i = itors.first; i != itors.second; ++i) {
541  const int distance = distance_between(target_loc,i->second);
542  const int defense = un.defense_modifier(map_.get_terrain(i->second));
543  //FIXME: suokko multiplied by 10 * get_caution(). ?
544  const double threat = (power_projection(i->second,enemy_dstsrc)*defense)/100;
545 
546  if(best_loc.valid() == false || (threat < std::max<double>(best_threat,max_acceptable_threat) && distance < best_distance)) {
547  best_loc = i->second;
548  best_threat = threat;
549  best_distance = distance;
550  }
551 
552  if(threat < max_acceptable_threat) {
553  mass_locations.insert(i->second);
554  }
555  }
556 
557  for(std::set<map_location>::const_iterator j = mass_locations.begin(); j != mass_locations.end(); ++j) {
558  if(*j != best_loc && distance_between(*j,best_loc) < 3) {
559  LOG_AI << "found mass-to-attack target... " << *j << " with value: " << value*4.0 << "\n";
560  targets.push_back(target(*j,value*4.0,target::TYPE::MASS));
561  best_target = targets.end() - 1;
562  }
563  }
564 
565  return std::pair<map_location,map_location>(loc,best_loc);
566  }
567  }
568 
569  for(std::vector<map_location>::reverse_iterator ri =
570  best_route.steps.rbegin(); ri != best_route.steps.rend(); ++ri) {
571 
572  //this is set to 'true' if we are hesitant to proceed because of enemy units,
573  //to rally troops around us.
574  bool is_dangerous = false;
575 
576  typedef std::multimap<map_location,map_location>::const_iterator Itor;
577  std::pair<Itor,Itor> its = dstsrc.equal_range(*ri);
578  while(its.first != its.second) {
579  if (its.first->second == best->get_location()) {
580  if(!should_retreat(its.first->first,best,fullmove_srcdst,fullmove_dstsrc,enemy_dstsrc,
581  get_caution())) {
582  double value = best_target->value - best->cost() / 20.0;
583 
584  if(value > 0.0 && best_target->type != target::TYPE::MASS) {
585  //there are enemies ahead. Rally troops around us to
586  //try to take the target
587  if(is_dangerous) {
588  LOG_AI << "found reinforcement target... " << its.first->first << " with value: " << value*2.0 << "\n";
589  targets.push_back(target(its.first->first,value*2.0,target::TYPE::BATTLE_AID));
590  }
591 
592  best_target->value = value;
593  } else {
594  targets.erase(best_target);
595  }
596 
597  LOG_AI << "Moving to " << its.first->first.x + 1 << "," << its.first->first.y + 1 << "\n";
598 
599  return std::pair<map_location,map_location>(its.first->second,its.first->first);
600  } else {
601  LOG_AI << "dangerous!\n";
602  is_dangerous = true;
603  }
604  }
605 
606  ++its.first;
607  }
608  }
609 
610  if(best != units_.end()) {
611  LOG_AI << "Could not make good move, staying still\n";
612 
613  //this sounds like the road ahead might be dangerous, and that's why we don't advance.
614  //create this as a target, attempting to rally units around
615  targets.push_back(target(best->get_location(), best_target->value));
616  best_target = targets.end() - 1;
617  return std::make_pair(best->get_location(), best->get_location());
618  }
619 
620  LOG_AI << "Could not find anywhere to move!\n";
621  return std::pair<map_location,map_location>();
622 }
623 
624 void move_to_targets_phase::access_points(const move_map& srcdst, const map_location& u, const map_location& dst, std::vector<map_location>& out)
625 {
627  const gamemap &map_ = resources::gameboard->map();
628  const unit_map::const_iterator u_it = units_.find(u);
629  if(u_it == units_.end()) {
630  return;
631  }
632 
633  // unit_map single_unit(u_it->first, u_it->second);
634 
635  const std::pair<move_map::const_iterator,move_map::const_iterator> locs = srcdst.equal_range(u);
636  for(move_map::const_iterator i = locs.first; i != locs.second; ++i) {
637  const map_location& loc = i->second;
638  if (int(distance_between(loc,dst)) <= u_it->total_movement()) {
640  const pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(*u_it, current_team());
641  pathfind::plain_route rt = a_star_search(loc, dst, u_it->total_movement(), &calc, map_.w(), map_.h(), &allowed_teleports);
642  if(rt.steps.empty() == false) {
643  out.push_back(loc);
644  }
645  }
646  }
647 }
648 
649 
650 double move_to_targets_phase::compare_groups(const std::set<map_location>& our_group, const std::set<map_location>& their_group, const std::vector<map_location>& battlefield) const
651 {
652  const double a = rate_group(our_group,battlefield);
653  const double b = std::max<double>(rate_group(their_group,battlefield),0.01);
654  return a/b;
655 }
656 
657 
658 void move_to_targets_phase::enemies_along_path(const std::vector<map_location>& route, const move_map& dstsrc, std::set<map_location>& res)
659 {
660  for(std::vector<map_location>::const_iterator i = route.begin(); i != route.end(); ++i) {
661  map_location adj[6];
662  get_adjacent_tiles(*i,adj);
663  for(size_t n = 0; n != 6; ++n) {
664  const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(adj[n]);
665  for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
666  res.insert(j->second);
667  }
668  }
669  }
670 }
671 
672 
673 map_location move_to_targets_phase::form_group(const std::vector<map_location>& route, const move_map& dstsrc, std::set<map_location>& res)
674 {
676  if(route.empty()) {
677  return map_location();
678  }
679 
680  std::vector<map_location>::const_iterator i;
681  for(i = route.begin(); i != route.end(); ++i) {
682  if(units_.count(*i) > 0) {
683  continue;
684  }
685 
686  size_t n = 0, nunits = res.size();
687 
688  const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*i);
689  for(move_map::const_iterator j = itors.first; j != itors.second; ++j) {
690  if(res.count(j->second) != 0) {
691  ++n;
692  } else {
693  const unit_map::const_iterator un = units_.find(j->second);
694  if(un == units_.end() || (un->can_recruit() && !get_leader_ignores_keep()) || un->movement_left() < un->total_movement()) {
695  continue;
696  }
697 
698  res.insert(j->second);
699  }
700  }
701 
702  //if not all our units can reach this position.
703  if(n < nunits) {
704  break;
705  }
706  }
707 
708  if(i != route.begin()) {
709  --i;
710  }
711 
712  return *i;
713 }
714 
715 
716 bool move_to_targets_phase::move_group(const map_location& dst, const std::vector<map_location>& route, const std::set<map_location>& units)
717 {
719  const gamemap &map_ = resources::gameboard->map();
720 
721  const std::vector<map_location>::const_iterator itor = std::find(route.begin(),route.end(),dst);
722  if(itor == route.end()) {
723  return false;
724  }
725 
726  LOG_AI << "group has " << units.size() << " members\n";
727 
729 
730  size_t direction = 0;
731 
732  //find the direction the group is moving in
733  if(itor+1 != route.end()) {
734  next = *(itor+1);
735  } else if(itor != route.begin()) {
736  next = *(itor-1);
737  }
738 
739  if(next.valid()) {
740  map_location adj[6];
741  get_adjacent_tiles(dst,adj);
742 
743  direction = std::find(adj,adj+6,next) - adj;
744  }
745 
746  std::deque<map_location> preferred_moves;
747  preferred_moves.push_back(dst);
748 
749  std::map<map_location,pathfind::paths> possible_moves;
750  move_map srcdst, dstsrc;
751  calculate_possible_moves(possible_moves,srcdst,dstsrc,false);
752 
753  bool gamestate_changed = false;
754 
755  for(std::set<map_location>::const_iterator i = units.begin(); i != units.end(); ++i) {
756  const unit_map::const_iterator un = units_.find(*i);
757  if(un == units_.end()) {
758  continue;
759  }
760 
761  map_location best_loc;
762  int best_defense = -1;
763  for(std::deque<map_location>::const_iterator j = preferred_moves.begin(); j != preferred_moves.end(); ++j) {
764  if(units_.count(*j)) {
765  continue;
766  }
767 
768  const std::pair<move_map::const_iterator,move_map::const_iterator> itors = dstsrc.equal_range(*j);
769  move_map::const_iterator m;
770  for(m = itors.first; m != itors.second; ++m) {
771  if(m->second == *i) {
772  break;
773  }
774  }
775 
776  if(m == itors.second) {
777  continue;
778  }
779 
780  int defense = un->defense_modifier(map_.get_terrain(*j));
781  if(best_loc.valid() == false || defense < best_defense) {
782  best_loc = *j;
783  best_defense = defense;
784  }
785  }
786 
787  if(best_loc.valid()) {
788  move_result_ptr move_res = execute_move_action(*i,best_loc);
789  gamestate_changed |= move_res->is_gamestate_changed();
790 
791 
792  //if we were ambushed or something went wrong, abort the group's movement.
793  if (!move_res->is_ok()) {
794  return gamestate_changed;
795  }
796 
797  preferred_moves.erase(std::find(preferred_moves.begin(),preferred_moves.end(),best_loc));
798 
799  //find locations that are 'perpendicular' to the direction of movement for further units to move to.
800  map_location adj[6];
801  get_adjacent_tiles(best_loc,adj);
802  for(size_t n = 0; n != 6; ++n) {
803  if(n != direction && ((n+3)%6) != direction && map_.on_board(adj[n]) &&
804  units_.count(adj[n]) == 0 && std::count(preferred_moves.begin(),preferred_moves.end(),adj[n]) == 0) {
805  preferred_moves.push_front(adj[n]);
806  LOG_AI << "added moves: " << adj[n].x + 1 << "," << adj[n].y + 1 << "\n";
807  }
808  }
809  } else {
810  LOG_AI << "Could not move group member to any of " << preferred_moves.size() << " locations\n";
811  }
812  }
813 
814  return gamestate_changed;
815 }
816 
817 
818 double move_to_targets_phase::rate_group(const std::set<map_location>& group, const std::vector<map_location>& battlefield) const
819 {
821  const gamemap &map_ = resources::gameboard->map();
822 
823  double strength = 0.0;
824  for(std::set<map_location>::const_iterator i = group.begin(); i != group.end(); ++i) {
825  const unit_map::const_iterator u = units_.find(*i);
826  if(u == units_.end()) {
827  continue;
828  }
829 
830  const unit &un = *u;
831 
832  int defense = 0;
833  for(std::vector<map_location>::const_iterator j = battlefield.begin(); j != battlefield.end(); ++j) {
834  defense += un.defense_modifier(map_.get_terrain(*j));
835  }
836 
837  defense /= battlefield.size();
838 
839  int best_attack = 0;
840  const std::vector<attack_type>& attacks = un.attacks();
841  for(std::vector<attack_type>::const_iterator a = attacks.begin(); a != attacks.end(); ++a) {
842  const int strength = a->num_attacks()*a->damage();
843  best_attack = std::max<int>(strength,best_attack);
844  }
845 
846  const int rating = (defense*best_attack*un.hitpoints())/(100*un.max_hitpoints());
847  strength += double(rating);
848  }
849 
850  return strength;
851 }
852 
853 
854 
856  const move_map& srcdst, const move_map& dstsrc, const move_map& enemy_dstsrc,
857  double caution)
858 {
859  if(caution <= 0.0) {
860  return false;
861  }
862 
863  double optimal_terrain = best_defensive_position(un->get_location(), dstsrc,
864  srcdst, enemy_dstsrc).chance_to_hit/100.0;
865  const double proposed_terrain =
866  un->defense_modifier(resources::gameboard->map().get_terrain(loc))/100.0;
867 
868  // The 'exposure' is the additional % chance to hit
869  // this unit receives from being on a sub-optimal defensive terrain.
870  const double exposure = proposed_terrain - optimal_terrain;
871 
872  const double our_power = power_projection(loc,dstsrc);
873  const double their_power = power_projection(loc,enemy_dstsrc);
874  return caution*their_power*(1.0+exposure) > our_power;
875 }
876 
877 } // end of namespace ai_default_rca
878 
879 } // end of namespace ai
virtual side_number get_side() const
Get the side number.
Definition: contexts.hpp:480
map_location form_group(const std::vector< map_location > &route, const move_map &dstsrc, std::set< map_location > &res)
move_to_targets_phase(rca_context &context, const config &cfg)
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator *calc, const size_t width, const size_t height, const teleport_map *teleports, bool border)
map_location loc
Definition: contexts.hpp:54
#define LOG_AI
unit_iterator end()
Definition: map.hpp:311
int max_hitpoints() const
Definition: unit.hpp:169
virtual void raise_user_interact() const
Definition: contexts.hpp:610
Definition: unit.hpp:95
virtual double get_caution() const
Definition: contexts.hpp:697
bool operator()(const rated_target &a, const rated_target &b) const
int movement_cost(const t_translation::t_terrain &terrain) const
Definition: unit.hpp:308
size_t count(const map_location &loc) const
Definition: map.hpp:306
double value
Definition: contexts.hpp:55
unit_iterator find_leader(int side)
Definition: map.cpp:297
int hitpoints() const
Definition: unit.hpp:168
variant map_
Definition: formula.cpp:306
remove_wrong_targets(const readonly_context &context)
Managing the AI-Game interaction - AI actions and their results.
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.hpp:274
double get_score() const
Get the usual score of the candidate action without re-evaluation.
Definition: rca.cpp:72
bool is_enemy(int n) const
Definition: team.hpp:247
virtual std::string get_grouping() const
Definition: contexts.hpp:745
unit_iterator begin()
Definition: map.hpp:308
static lg::log_domain log_ai_testing_ca_move_to_targets("ai/ca/move_to_targets")
GLsizeiptr const GLvoid GLenum usage
Definition: glew.h:1649
GLdouble GLdouble t
Definition: glew.h:1366
int defense_modifier(const t_translation::t_terrain &terrain) const
Definition: unit.cpp:1520
bool match(const map_location &loc) const
Definition: filter.cpp:364
static double getNoPathValue()
Definition: pathfind.hpp:64
virtual double power_projection(const map_location &loc, const move_map &dstsrc) const
Function which finds how much 'power' a side can attack a certain location with.
Definition: contexts.hpp:806
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
virtual double get_scout_village_targeting() const
Definition: contexts.hpp:860
double cost(const map_location &loc, const double) const
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:47
std::pair< map_location, map_location > choose_move(std::vector< target > &targets, const move_map &srcdst, const move_map &dstsrc, const move_map &enemy_dstsrc)
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:57
std::vector< map_location > steps
Definition: pathfind.hpp:135
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
std::vector< team > * teams
Definition: resources.cpp:29
virtual std::vector< target > find_targets(const move_map &enemy_dstsrc)
Definition: contexts.hpp:230
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:132
bool valid() const
Definition: location.hpp:69
virtual const std::vector< target > & additional_targets() const
Definition: contexts.hpp:206
size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.hpp:357
TYPE type
Definition: contexts.hpp:57
GLsizei const GLfloat * value
Definition: glew.h:1817
std::vector< target >::iterator tg
GLenum GLenum dst
Definition: glew.h:2392
int w() const
Effective map width.
Definition: map.hpp:105
game_board * gameboard
Definition: resources.cpp:20
Composite AI with turn sequence which is a vector of stages.
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
Encapsulates the map of the game.
Definition: map.hpp:37
virtual bool get_leader_ignores_keep() const
Definition: contexts.hpp:776
#define log_scope2(domain, description)
Definition: log.hpp:186
virtual const move_map & get_srcdst() const
Definition: contexts.hpp:854
virtual void calculate_possible_moves(std::map< map_location, pathfind::paths > &possible_moves, move_map &srcdst, move_map &dstsrc, bool enemy, bool assume_full_movement=false, const terrain_filter *remove_destinations=nullptr) const
Definition: contexts.hpp:588
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:135
double rate_group(const std::set< map_location > &group, const std::vector< map_location > &battlefield) const
GLuint GLuint GLsizei count
Definition: glew.h:1221
virtual const move_map & get_enemy_dstsrc() const
Definition: contexts.hpp:709
const std::vector< attack_type > & attacks() const
Definition: unit.hpp:271
int move_cost
Movement cost for reaching the end of the route.
Definition: pathfind.hpp:137
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:47
virtual bool get_simple_targeting() const
Definition: contexts.hpp:866
Encapsulates the map of the game.
Definition: location.hpp:38
GLuint res
Definition: glew.h:9258
#define DBG_AI
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
int h() const
Effective map height.
Definition: map.hpp:108
size_t i
Definition: function.cpp:1057
defensive_position const & best_defensive_position(const map_location &unit, const move_map &dstsrc, const move_map &srcdst, const move_map &enemy_dstsrc) const
Definition: contexts.hpp:622
double rate_target(const target &tg, const unit_map::iterator &u, const move_map &dstsrc, const move_map &enemy_dstsrc, const pathfind::plain_route &rt)
rate a target, but can also return the maximal possible rating by passing a dummy route ...
virtual move_result_ptr execute_move_action(const map_location &from, const map_location &to, bool remove_movement=true, bool unreach_is_ok=false)
Definition: contexts.hpp:1033
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
void enemies_along_path(const std::vector< map_location > &route, const move_map &dstsrc, std::set< map_location > &res)
move_cost_calculator(const unit &u, const gamemap &map, const unit_map &units, const move_map &enemy_dstsrc)
boost::shared_ptr< std::vector< unit_const_ptr > > units_
Definition: dialogs.cpp:100
#define WRN_AI
virtual const team & current_team() const
Definition: contexts.hpp:539
virtual const gamemap & map() const
Definition: game_board.hpp:98
t_translation::t_terrain get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:341
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:467
#define next(ls)
Definition: llex.cpp:27
Strategic movement routine, for experimentation.
bool move_group(const map_location &dst, const std::vector< map_location > &route, const std::set< map_location > &units)
GLclampd n
Definition: glew.h:5903
GLboolean GLuint group
Definition: glew.h:2589
const GLdouble * m
Definition: glew.h:6968
bool find(E event, F functor)
Tests whether an event handler is available.
boost::shared_ptr< move > move_ptr
Definition: typedefs.hpp:76
Standard logging facilities (interface).
Container associating units to locations.
Definition: map.hpp:90
double compare_groups(const std::set< map_location > &our_group, const std::set< map_location > &their_group, const std::vector< map_location > &battlefield) const
unit_iterator find(size_t id)
Definition: map.cpp:285
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units)
Definition: teleport.cpp:233
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
rated_target(const std::vector< target >::iterator &t, double r)
This module contains various pathfinding functions and utilities.
bool should_retreat(const map_location &loc, const unit_map::const_iterator &un, const move_map &srcdst, const move_map &dstsrc, const move_map &enemy_dstsrc, double caution)
unit_map * units
Definition: resources.cpp:35
void access_points(const move_map &srcdst, const map_location &u, const map_location &dst, std::vector< map_location > &out)
virtual const move_map & get_dstsrc() const
Definition: contexts.hpp:703
GLenum target
Definition: glew.h:5190
virtual void execute()
Execute the candidate action.