The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
function_table.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2017 by Bartosz Waresiak <dragonking@o2.pl>
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 #include <queue>
17 #include <set>
18 #include <utility>
19 #include <vector>
20 
21 #include "ai/formula/ai.hpp"
24 
25 #include "ai/default/contexts.hpp"
26 
28 #include "attack_prediction.hpp"
29 #include "filesystem.hpp"
30 #include "game_board.hpp"
31 #include "display.hpp"
32 #include "log.hpp"
33 #include "map/label.hpp"
34 #include "map/map.hpp"
35 #include "pathfind/teleport.hpp"
36 #include "replay.hpp"
37 #include "resources.hpp"
38 #include "color.hpp"
39 #include "terrain/filter.hpp"
40 #include "units/unit.hpp"
41 #include "pathfind/pathfind.hpp"
42 
43 static lg::log_domain log_formula_ai("ai/engine/fai");
44 #define LOG_AI LOG_STREAM(info, log_formula_ai)
45 #define WRN_AI LOG_STREAM(warn, log_formula_ai)
46 #define ERR_AI LOG_STREAM(err, log_formula_ai)
47 
48 namespace wfl {
49 using ai::formula_ai;
50 
51 namespace {
52 
53 /*
54  * unit adapters let us treat unit and unit_type the same if we want to get access to attacks or movement cost
55  */
56 class unit_adapter {
57  public:
58  unit_adapter(const variant& arg) : unit_type_(), unit_() {
59  auto unit = arg.try_convert<unit_callable>();
60 
61  if (unit) {
62  unit_ = &unit->get_unit();
63  } else {
64  unit_type_ = &(arg.convert_to<unit_type_callable>()->get_unit_type());
65  }
66  }
67 
68  int damage_from(const attack_type& attack) const {
69  if(unit_type_ != nullptr) {
71  } else {
72  return unit_->damage_from(attack, false, map_location());
73  }
74  }
75 
76  const_attack_itors attacks() const {
77  if(unit_type_ != nullptr) {
78  return unit_type_->attacks();
79  } else {
80  return unit_->attacks();
81  }
82  }
83 
84  int movement_cost(const t_translation::terrain_code & terrain) const {
85  if(unit_type_ != nullptr) {
86  return unit_type_->movement_type().movement_cost(terrain);
87  } else {
88  return unit_->movement_cost(terrain);
89  }
90  }
91 
92 
93  private:
95  const unit* unit_;
96 };
97 
98 
99 class distance_to_nearest_unowned_village_function : public function_expression {
100 public:
101  distance_to_nearest_unowned_village_function(const args_list& args, const formula_ai& ai)
102  : function_expression("distance_to_nearest_unowned_village", args, 1, 1), ai_(ai) {
103  }
104 
105 private:
106  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
107  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "distance_to_nearest_unowned_village:location")).convert_to<location_callable>()->loc();
108  int best = 1000000;
109  const std::vector<map_location>& villages = resources::gameboard->map().villages();
110  const std::set<map_location>& my_villages = ai_.current_team().villages();
111  for(std::vector<map_location>::const_iterator i = villages.begin(); i != villages.end(); ++i) {
112  int distance = distance_between(loc, *i);
113  if(distance < best) {
114  if(my_villages.count(*i) == 0) {
115  best = distance;
116  }
117  }
118  }
119 
120  return variant(best);
121  }
122 
123  const formula_ai& ai_;
124 };
125 
126 static unsigned search_counter;
127 
128 ///@todo 1.8: document
129 class calculate_map_ownership_function : public function_expression {
130 public:
131  calculate_map_ownership_function(const args_list& args, const formula_ai& ai)
132  : function_expression("calculate_map_ownership", args, 2, 5), ai_(ai) {
133  }
134 
135 private:
136 
137  struct indexer {
138  int w, h;
139  indexer(int a, int b) : w(a), h(b) { }
140  int operator()(const map_location& loc) const {
141  return loc.y * w + loc.x;
142  }
143  };
144 
145  struct node {
148 
149  /**
150  * If equal to search_counter, the node is off the list.
151  * If equal to search_counter + 1, the node is on the list.
152  * Otherwise it is outdated.
153  */
154  unsigned in;
155 
156  node(int moves, const map_location &loc)
157  : movement_cost_(moves)
158  , loc_(loc)
159  , in(0)
160  {
161  }
162 
163  node()
164  : movement_cost_(0)
165  , loc_()
166  , in(0)
167  {
168  }
169 
170  bool operator<(const node& o) const {
171  return movement_cost_ < o.movement_cost_;
172  }
173  };
174 
175  struct comp {
176  const std::vector<node>& nodes;
177  comp(const std::vector<node>& n) : nodes(n) { }
178  bool operator()(int l, int r) const {
179  return nodes[r] < nodes[l];
180  }
181  };
182 
183  void find_movemap(const unit_adapter& u, const map_location& loc,
184  std::vector<int>& scores, bool allow_teleport) const
185  {
186  const std::set<map_location>& teleports = allow_teleport ? ai_.current_team().villages() : std::set<map_location>();
187 
188  const gamemap& map = resources::gameboard->map();
189 
190  std::vector<map_location> locs(6 + teleports.size());
191  std::copy(teleports.begin(), teleports.end(), locs.begin() + 6);
192 
193  search_counter += 2;
194  if (search_counter == 0) search_counter = 2;
195 
196  static std::vector<node> nodes;
197  nodes.resize(map.w() * map.h());
198 
199  indexer index(map.w(), map.h());
200  comp node_comp(nodes);
201 
202  nodes[index(loc)] = node(0, loc);
203  std::vector<int> pq;
204  pq.push_back(index(loc));
205  while (!pq.empty()) {
206  node& n = nodes[pq.front()];
207  std::pop_heap(pq.begin(), pq.end(), node_comp);
208  pq.pop_back();
209  n.in = search_counter;
210 
211  get_adjacent_tiles(n.loc_, &locs[0]);
212  for (int i = teleports.count(n.loc_) ? locs.size() : 6; i-- > 0; ) {
213  if (!locs[i].valid(map.w(), map.h())) continue;
214 
215  node& next = nodes[index(locs[i])];
216  bool next_visited = next.in - search_counter <= 1u;
217 
218  // test if the current path to locs[i] is better than this one could possibly be.
219  // we do this a couple more times below
220  if (next_visited && !(n < next) ) continue;
221  const int move_cost = u.movement_cost(map[locs[i]]);
222 
223  node t = node(n.movement_cost_ + move_cost, locs[i]);
224 
225  if (next_visited && !(t < next) ) continue;
226 
227  bool in_list = next.in == search_counter + 1;
228  t.in = search_counter + 1;
229  next = t;
230 
231  // if already in the priority queue then we just update it, else push it.
232  if (in_list) {
233  std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp);
234  } else {
235  pq.push_back(index(locs[i]));
236  std::push_heap(pq.begin(), pq.end(), node_comp);
237  }
238  }
239  }
240 
241  for (int x = 0; x < map.w(); ++x) {
242  for (int y = 0; y < map.h(); ++y)
243  {
244  int i = y * map.w() + x;
245  const node &n = nodes[i];
246  scores[i] = scores[i] + n.movement_cost_;
247  //std::cout << x << "," << y << ":" << n.movement_cost << std::endl;
248  }
249  }
250  }
251 
252  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
253  int w = resources::gameboard->map().w();
254  int h = resources::gameboard->map().h();
255 
256  const variant units_input = args()[0]->evaluate(variables,fdb);
257  const variant leaders_input = args()[1]->evaluate(variables,fdb);
258 
259  int enemy_tolerance = 3;
260  if( args().size() > 2 )
261  enemy_tolerance = args()[2]->evaluate(variables,fdb).as_int();
262 
263  int enemy_border_tolerance = 5;
264  if( args().size() > 3 )
265  enemy_border_tolerance = args()[3]->evaluate(variables,fdb).as_int();
266 
267  int ally_tolerance = 3;
268  if( args().size() > 4 )
269  ally_tolerance = args()[4]->evaluate(variables,fdb).as_int();
270 
271  if( !units_input.is_list() )
272  return variant();
273 
274  size_t number_of_teams = units_input.num_elements();
275 
276  std::vector< std::vector<int> > scores( number_of_teams );
277 
278  for( size_t i = 0; i< number_of_teams; ++i)
279  scores[i].resize(w*h);
280 
281 // for(unit_map::const_iterator i = resources::gameboard->units().begin(); i != resources::gameboard->units().end(); ++i) {
282 // unit_counter[i->second.side()-1]++;
283 // unit_adapter unit(i->second);
284 // find_movemap( resources::gameboard->map(), resources::gameboard->units(), unit, i->first, scores[i->second.side()-1], ai_.resources::gameboard->teams() , true );
285 // }
286 
287  for(size_t side = 0 ; side < units_input.num_elements() ; ++side) {
288  if( leaders_input[side].is_empty() )
289  continue;
290 
291  const map_location loc = leaders_input[side][0].convert_to<location_callable>()->loc();
292  const variant units_of_side = units_input[side];
293 
294  for(size_t unit_it = 0 ; unit_it < units_of_side.num_elements() ; ++unit_it) {
295  unit_adapter unit(units_of_side[unit_it]);
296  find_movemap( unit, loc, scores[side], true );
297  }
298  }
299 
300  size_t index = 0;
301  for( std::vector< std::vector<int> >::iterator i = scores.begin() ; i != scores.end() ; ++i) {
302  for( std::vector<int>::iterator j = i->begin() ; j != i->end() ; ++j ) {
303  if(units_input[index].num_elements() != 0) {
304  *j /= units_input[index].num_elements();
305  } else {
306  *j = 0;
307  }
308  }
309 
310  ++index;
311  }
312  //std::vector<variant> res;
313  std::map<variant, variant> res;
314 
315  size_t current_side = ai_.get_side() - 1 ;
316 
317  std::vector<int> enemies;
318  std::vector<int> allies;
319 
320  for(size_t side = 0 ; side < units_input.num_elements() ; ++side) {
321  if( side == current_side)
322  continue;
323 
324  if( ai_.current_team().is_enemy(side+1) ) {
325  if( !leaders_input[side].is_empty() )
326  enemies.push_back(side);
327  } else {
328  if( !leaders_input[side].is_empty() )
329  allies.push_back(side);
330  }
331  }
332 
333  //calculate_map_ownership( recruits_of_side, map(units_of_side, 'units', map( filter(units, leader), loc) ) )
334  //map(, debug_label(key,value))
335  for (int x = 0; x < w; ++x) {
336  for (int y = 0; y < h; ++y)
337  {
338  int i = y * w + x;
339  bool valid = true;
340  bool enemy_border = false;
341 
342  if( scores[current_side][i] > 98 )
343  continue;
344 
345  for (int side : enemies) {
346  int diff = scores[current_side][i] - scores[side][i];
347  if ( diff > enemy_tolerance) {
348  valid = false;
349  break;
350  } else if( std::abs(diff) < enemy_border_tolerance )
351  enemy_border = true;
352  }
353 
354  if( valid ) {
355  for (int side : allies) {
356  if ( scores[current_side][i] - scores[side][i] > ally_tolerance ) {
357  valid = false;
358  break;
359  }
360  }
361  }
362 
363  if( valid ) {
364  if( enemy_border )
365  res.emplace(variant(std::make_shared<location_callable>(map_location(x, y))), variant(scores[0][i] + 10000));
366  else
367  res.emplace(variant(std::make_shared<location_callable>(map_location(x, y))), variant(scores[0][i]));
368  }
369  }
370  }
371  return variant(res);
372  }
373 
374  const formula_ai& ai_;
375 };
376 
377 
378 class nearest_loc_function : public function_expression {
379 public:
380  nearest_loc_function(const args_list& args)
381  : function_expression("nearest_loc", args, 2, 2)
382  {
383  }
384 
385 private:
386  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
387  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_loc:location")).convert_to<location_callable>()->loc();
388  variant items = args()[1]->evaluate(variables,add_debug_info(fdb,1,"nearest_loc:locations"));
389  int best = 1000000;
390  int best_i = -1;
391 
392  for(size_t i = 0; i < items.num_elements(); ++i) {
393 
394  const map_location move_loc = items[i].convert_to<location_callable>()->loc();
395  int distance = distance_between(loc, move_loc);
396 
397  if(distance < best) {
398  best = distance;
399  best_i = i;
400  }
401  }
402 
403  if( best_i != -1)
404  return variant(std::make_shared<location_callable>(items[best_i].convert_to<location_callable>()->loc()));
405  else
406  return variant();
407  }
408 };
409 
410 
411 /** FormulaAI function to run fai script from file. Usable from in-game console.
412 * arguments[0] - required file name, follows the usual wml convention
413 */
414 class run_file_function : public function_expression {
415 public:
416  explicit run_file_function(const args_list& args, formula_ai& ai)
417  : function_expression("run_file", args, 1, 1), ai_(ai)
418  {}
419 private:
420  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
421  const args_list& arguments = args();
422  const variant var0 = arguments[0]->evaluate(variables,add_debug_info(fdb,0,"run_file:file"));
423  const std::string filename = var0.string_cast();
424 
425  //NOTE: get_wml_location also filters file path to ensure it doesn't contain things like "../../top/secret"
427  if(path.empty()) {
428  ERR_AI << "run_file : not found [" << filename <<"]"<< std::endl;
429  return variant(); //no suitable file
430  }
431 
432  std::string formula_string = filesystem::read_file(path);
433  //need to get function_table from somewhere or delegate to someone who has access to it
434  formula_ptr parsed_formula = ai_.create_optional_formula(formula_string);
435  if(parsed_formula == formula_ptr()) {
436  ERR_AI << "run_file : unable to create formula"<< std::endl;
437  return variant(); //was unable to create a formula from file
438  }
439  return parsed_formula->evaluate(variables,add_debug_info(fdb,-1,"run_file:formula_from_file"));
440  }
441 
442  formula_ai& ai_;
443 };
444 
445 
446 class castle_locs_function : public function_expression {
447 public:
448  castle_locs_function(const args_list& args)
449  : function_expression("castle_locs", args, 1, 1)
450  {
451  }
452 
453 private:
454  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
455  const map_location starting_loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "castle_locs:location")).convert_to<location_callable>()->loc();
456 
457  //looks like reimplementing a generic graph search algorithm to me
458  std::set< map_location > visited_locs;
459  std::queue< map_location > queued_locs;
460 
461  queued_locs.push(starting_loc);
462 
463  while( !queued_locs.empty() ) {
464  const map_location loc = queued_locs.front();
465  queued_locs.pop();
466 
467  if ( visited_locs.find( loc ) != visited_locs.end() )
468  continue;
469 
470  visited_locs.insert(loc);
471 
472  map_location adj[6];
473  get_adjacent_tiles(loc, adj);
474 
475  for(int n = 0; n != 6; ++n) {
476  if (resources::gameboard->map().on_board(adj[n]) && visited_locs.find( adj[n] ) == visited_locs.end() ) {
477  if (resources::gameboard->map().get_terrain_info(adj[n]).is_keep() ||
479  queued_locs.push(adj[n]);
480  }
481  }
482  }
483  }
484 
485  if ( !resources::gameboard->map().get_terrain_info(starting_loc).is_keep() &&
486  !resources::gameboard->map().get_terrain_info(starting_loc).is_castle() )
487  visited_locs.erase(starting_loc);
488 
489  std::vector<variant> res;
490  for (const map_location& ml : visited_locs) {
491  res.emplace_back(std::make_shared<location_callable>( ml ));
492  }
493 
494  return variant(res);
495  }
496 };
497 
498 
499 /**
500  * timeofday_modifer formula function. Returns combat modifier, taking
501  * alignment, illuminate, time of day and fearless trait into account.
502  * 'leadership' and 'slowed' are not taken into account.
503  * arguments[0] - unit
504  * arguments[1] - location (optional, defaults to unit's current location.
505  */
506 class timeofday_modifier_function : public function_expression {
507 public:
508  timeofday_modifier_function(const args_list& args)
509  : function_expression("timeofday_modifier", args, 1, 2)
510  {
511  }
512 private:
513  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
514  variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"timeofday_modifier:unit"));
515 
516  if( u.is_null() ) {
517  return variant();
518  }
519 
520  auto u_call = u.try_convert<unit_callable>();
521 
522  if(!u_call) {
523  return variant();
524  }
525 
526  const unit& un = u_call->get_unit();
527 
528  map_location loc;
529 
530  if(args().size()==2) {
531  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "timeofday_modifier:location")).convert_to<location_callable>()->loc();
532  }
533 
534  if(!loc.valid()) {
535  loc = u_call->get_location();
536  }
537 
538  return variant(combat_modifier(resources::gameboard->units(), resources::gameboard->map(), loc, un.alignment(), un.is_fearless()));
539  }
540 };
541 
542 
543 class nearest_keep_function : public function_expression {
544 public:
545  nearest_keep_function(const args_list& args, const formula_ai& ai)
546  : function_expression("nearest_keep", args, 1, 1), ai_(ai) {
547  }
548 
549 private:
550  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
551  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_keep:location")).convert_to<location_callable>()->loc();
552  int best = 1000000;
553  int best_i = -1;
554 
555  ai_.get_keeps();
556  int size = ai_.get_keeps_cache().num_elements();
557 
558  for( int i = 0 ; i < size; ++i) {
559  int distance = distance_between(loc, ai_.get_keeps_cache()[i].convert_to<location_callable>()->loc() );
560  if(distance < best)
561  {
562  best = distance;
563  best_i = i;
564  }
565  }
566 
567  if( best_i != -1)
568  return variant(std::make_shared<location_callable>(ai_.get_keeps_cache()[best_i].convert_to<location_callable>()->loc()));
569  else
570  return variant();
571  }
572 
573  const formula_ai& ai_;
574 };
575 
576 
577 /**
578 * Find suitable keep for unit at location
579 * arguments[0] - location for unit on which the suitable keep is to be found
580 */
581 class suitable_keep_function : public function_expression {
582 public:
583  suitable_keep_function(const args_list& args, formula_ai& ai)
584  : function_expression("suitable_keep", args, 1, 1), ai_(ai) {
585  }
586 
587 private:
588  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
589  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "suitable_keep:location")).convert_to<location_callable>()->loc();
591  const unit_map::const_iterator u = units.find(loc);
592  if (u == units.end()){
593  return variant();
594  }
595  const pathfind::paths unit_paths(*u, false, true, ai_.current_team());
596  return variant(std::make_shared<location_callable>(ai_.suitable_keep(loc,unit_paths)));
597  }
598 
599  formula_ai& ai_;
600 };
601 
602 
603 class find_shroud_function : public function_expression {
604 public:
605  find_shroud_function(const args_list& args, const formula_ai& ai)
606  : function_expression("find_shroud", args, 0, 1), ai_(ai) {
607  }
608 
609 private:
610  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
611  std::vector<variant> vars;
612  int w,h;
613 
614  if(args().size()==1) {
615  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "find_shroud:gamemap")).convert_to<gamemap_callable>()->get_gamemap();
616  w = m.w();
617  h = m.h();
618  } else {
619  w = resources::gameboard->map().w();
620  h = resources::gameboard->map().h();
621  }
622 
623  for(int i = 0; i < w; ++i)
624  for(int j = 0; j < h; ++j) {
625  if(ai_.current_team().shrouded(map_location(i,j)))
626  vars.emplace_back(std::make_shared<location_callable>(map_location(i, j)));
627  }
628 
629  return variant(vars);
630  }
631 
632  const formula_ai& ai_;
633 };
634 
635 
636 class close_enemies_function : public function_expression {
637 public:
638  close_enemies_function(const args_list& args, const formula_ai& ai)
639  : function_expression("close_enemies", args, 2, 2), ai_(ai) {
640  }
641 
642 private:
643  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
644  std::vector<variant> vars;
645  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "close_enemies:location")).convert_to<location_callable>()->loc();
646  int range_s = args()[1]->evaluate(variables,add_debug_info(fdb,1,"close_enemies:distance")).as_int();
647  if (range_s < 0) {
648  WRN_AI << "close_enemies_function: range is negative (" << range_s << ")" << std::endl;
649  range_s = 0;
650  }
651  size_t range = static_cast<size_t>(range_s);
654  while (un != end) {
655  if (distance_between(loc, un->get_location()) <= range) {
656  if (un->side() != ai_.get_side()) {//fixme: ignores allied units
657  vars.emplace_back(std::make_shared<unit_callable>(*un));
658  }
659  }
660  ++un;
661  }
662  return variant(vars);
663  }
664 
665  const formula_ai& ai_;
666 };
667 
668 class calculate_outcome_function : public function_expression {
669 public:
670  calculate_outcome_function(const args_list& args)
671  : function_expression( "calculate_outcome", args, 3, 4)
672  {
673  }
674 
675 private:
676  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
677  std::vector<variant> vars;
678  int weapon;
679  if (args().size() > 3) weapon = args()[3]->evaluate(variables,add_debug_info(fdb,3,"calculate_outcome:weapon")).as_int();
680  else weapon = -1;
681 
682  const unit_map& units = resources::gameboard->units();
683  map_location attacker_location =
684  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "calculate_outcome:attacker_current_location")).convert_to<location_callable>()->loc();
685  if(units.count(attacker_location) == 0) {
686  ERR_AI << "Performing calculate_outcome() with non-existent attacker at (" <<
687  attacker_location.wml_x() << "," << attacker_location.wml_y() << ")\n";
688  return variant();
689  }
690 
691  map_location defender_location =
692  args()[2]->evaluate(variables,add_debug_info(fdb, 2, "calculate_outcome:defender_location")).convert_to<location_callable>()->loc();
693  if(units.count(defender_location) == 0) {
694  ERR_AI << "Performing calculate_outcome() with non-existent defender at (" <<
695  defender_location.wml_x() << "," << defender_location.wml_y() << ")\n";
696  return variant();
697  }
698 
699  battle_context bc(units, args()[1]->evaluate(variables, add_debug_info(fdb, 1, "calculate_outcome:attacker_attack_location")).convert_to<location_callable>()->loc(),
700  defender_location, weapon, -1, 1.0, nullptr, &*units.find(attacker_location));
701  std::vector<double> hp_dist = bc.get_attacker_combatant().hp_dist;
702  std::vector<double>::iterator it = hp_dist.begin();
703  int i = 0;
704  std::vector<variant> hitLeft;
705  std::vector<variant> prob;
706  while (it != hp_dist.end()) {
707  if (*it != 0) {
708  hitLeft.emplace_back(i);
709  prob.emplace_back(int(*it*10000));
710  }
711  ++it;
712  ++i;
713  }
714  std::vector<variant> status;
715  if (bc.get_attacker_combatant().poisoned != 0)
716  status.emplace_back("Poisoned");
717  if (bc.get_attacker_combatant().slowed != 0)
718  status.emplace_back("Slowed");
719  if (bc.get_defender_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_attacker_stats().hp)
720  status.emplace_back("Stoned");
721  if (bc.get_defender_stats().plagues && hitLeft[0].as_int() == 0)
722  status.emplace_back("Zombiefied");
723  vars.emplace_back(std::make_shared<outcome_callable>(hitLeft, prob, status));
724  hitLeft.clear();
725  prob.clear();
726  status.clear();
727  hp_dist = bc.get_defender_combatant().hp_dist;
728  it = hp_dist.begin();
729  i = 0;
730  while (it != hp_dist.end()) {
731  if (*it != 0) {
732  hitLeft.emplace_back(i);
733  prob.emplace_back(int(*it*10000));
734  }
735  ++it;
736  ++i;
737  }
738  if (bc.get_defender_combatant().poisoned != 0)
739  status.emplace_back("Poisoned");
740  if (bc.get_defender_combatant().slowed != 0)
741  status.emplace_back("Slowed");
742  if (bc.get_attacker_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_defender_stats().hp)
743  status.emplace_back("Stoned");
744  if (bc.get_attacker_stats().plagues && hitLeft[0].as_int() == 0)
745  status.emplace_back("Zombiefied");
746  vars.emplace_back(std::make_shared<outcome_callable>(hitLeft, prob, status));
747  return variant(vars);
748  }
749 };
750 
751 
752 class outcomes_function : public function_expression {
753 public:
754  outcomes_function(const args_list& args)
755  : function_expression("outcomes", args, 1, 1)
756  {
757  }
758 
759 private:
760  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
761  variant attack = args()[0]->evaluate(variables,add_debug_info(fdb,0,"outcomes:attack"));
762  auto analysis = attack.convert_to<ai::attack_analysis>();
763  //unit_map units_with_moves(resources::gameboard->units());
764  //typedef std::pair<map_location, map_location> mv;
765  //for(const mv &m : analysis->movements) {
766  // units_with_moves.move(m.first, m.second);
767  //}
768 
769  std::vector<variant> vars;
770  if(analysis->chance_to_kill > 0.0) {
771  //unit_map units(units_with_moves);
772  //units.erase(analysis->target);
773  vars.emplace_back(std::make_shared<position_callable>(/*&units,*/ static_cast<int>(analysis->chance_to_kill*100)));
774 
775  }
776 
777  if(analysis->chance_to_kill < 1.0) {
778  //unit_map units(units_with_moves);
779  vars.emplace_back(std::make_shared<position_callable>(/*&units,*/ static_cast<int>(100 - analysis->chance_to_kill*100)));
780  }
781 
782  return variant(vars);
783  }
784 };
785 
786 //class evaluate_for_position_function : public function_expression {
787 //public:
788 // evaluate_for_position_function(const args_list& args, formula_ai& ai)
789 // : function_expression("evaluate_for_position", args, 2, 2), ai_(ai) {
790 // }
791 //
792 //private:
793 // variant execute(const formula_callable& variables, formula_debugger *fdb) const {
794 // variant position = args()[0]->evaluate(variables,add_debug_info(fdb,0,"evaluate_for_position:position"));
795 // ai_.store_outcome_position(position);
796 // position_callable* pos = position.convert_to<position_callable>();
797 // position_callable::swapper swapper(ai_, *pos);
798 // return args()[1]->evaluate(variables,add_debug_info(fdb,1,"evaluate_for_position:formula"));
799 // }
800 //
801 // formula_ai& ai_;
802 //};
803 
804 
805 
806 class rate_action_function : public function_expression {
807 public:
808  explicit rate_action_function(const args_list& args, const formula_ai &ai)
809  : function_expression("rate_action", args, 1, 1), ai_(ai)
810  {}
811 private:
812  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
813  variant act = args()[0]->evaluate(variables,add_debug_info(fdb,0,"rate_action:action"));
814  auto analysis = act.convert_to<ai::attack_analysis>();
815 
816  return variant(analysis->rating(ai_.get_aggression(),ai_)*1000,variant::DECIMAL_VARIANT);
817  }
818  const formula_ai &ai_;
819 };
820 
821 
822 
823 class recall_function : public function_expression {
824 public:
825  explicit recall_function(const args_list& args)
826  : function_expression("recall", args, 1, 2)
827  {}
828 private:
829  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
830  const std::string id = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recall:id")).as_string();
831  map_location loc;
832  if(args().size() >= 2) {
833  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "recall:location")).convert_to<location_callable>()->loc();
834  }
835 
836  return variant(std::make_shared<recall_callable>(loc, id));
837  }
838 };
839 
840 
841 class recruit_function : public function_expression {
842 public:
843  explicit recruit_function(const args_list& args)
844  : function_expression("recruit", args, 1, 2)
845  {}
846 private:
847  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
848  const std::string type = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recruit:type")).as_string();
849  map_location loc;
850  if(args().size() >= 2) {
851  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "recruit:location")).convert_to<location_callable>()->loc();
852  }
853 
854  return variant(std::make_shared<recruit_callable>(loc, type));
855  }
856 };
857 
858 
859 class shortest_path_function : public function_expression {
860 public:
861  explicit shortest_path_function(const args_list& args, const formula_ai& ai)
862  : function_expression("shortest_path", args, 2, 3), ai_(ai)
863  {}
864 
865 private:
866  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
867 
868  std::vector<variant> locations;
869 
870  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "shortest_path:src")).convert_to<location_callable>()->loc();
871  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "shortest_path:dst")).convert_to<location_callable>()->loc();
872  map_location unit_loc;
873 
874  if( src == dst )
875  return variant(locations);
876 
877  if(args().size() > 2)
878  unit_loc = args()[2]->evaluate(variables,add_debug_info(fdb,2,"shortest_path:unit_location")).convert_to<location_callable>()->loc();
879  else
880  unit_loc = src;
881 
882  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
883 
884  if( unit_it == resources::gameboard->units().end() ) {
885  std::ostringstream str;
886  str << "shortest_path function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
887  throw formula_error( str.str(), "", "", 0);
888  }
889 
890  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
891 
892  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
893 
894  if( route.steps.size() < 2 ) {
895  return variant(locations);
896  }
897 
898  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
899  locations.emplace_back(std::make_shared<location_callable>(*loc_iter));
900  }
901 
902  return variant(locations);
903  }
904 
905  const formula_ai& ai_;
906 };
907 
908 
909 class simplest_path_function : public function_expression {
910 public:
911  explicit simplest_path_function(const args_list& args, const formula_ai& ai)
912  : function_expression("simplest_path", args, 2, 3), ai_(ai)
913  {}
914 
915 private:
916  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
917 
918  std::vector<variant> locations;
919 
920  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "simplest_path:src")).convert_to<location_callable>()->loc();
921  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "simplest_path:dst")).convert_to<location_callable>()->loc();
922  map_location unit_loc;
923 
924  if( src == dst )
925  return variant(locations);
926 
927  if(args().size() > 2)
928  unit_loc = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "simplest_path:unit_location")).convert_to<location_callable>()->loc();
929  else
930  unit_loc = src;
931 
932  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
933 
934  if( unit_it == resources::gameboard->units().end() ) {
935  std::ostringstream str;
936  str << "simplest_path function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
937  throw formula_error( str.str(), "", "", 0);
938  }
939 
940  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
941 
943 
944  pathfind::plain_route route = pathfind::a_star_search(src, dst, 1000.0, em_calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
945 
946  if( route.steps.size() < 2 ) {
947  return variant(locations);
948  }
949 
950  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
951  if (unit_it->movement_cost((resources::gameboard->map())[*loc_iter]) < movetype::UNREACHABLE )
952  locations.emplace_back(std::make_shared<location_callable>(*loc_iter));
953  else
954  break;
955  }
956 
957  return variant(locations);
958  }
959 
960  const formula_ai& ai_;
961 };
962 
963 
964 
965 class next_hop_function : public function_expression {
966 public:
967  explicit next_hop_function(const args_list& args, const formula_ai& ai)
968  : function_expression("next_hop", args, 2, 3), ai_(ai)
969  {}
970 
971 private:
972  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
973 
974  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "next_hop:src")).convert_to<location_callable>()->loc();
975  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "next_hop:dst")).convert_to<location_callable>()->loc();
976  map_location unit_loc;
977 
978  if( src == dst )
979  return variant();
980 
981  if(args().size() > 2)
982  unit_loc = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "next_hop:unit_location")).convert_to<location_callable>()->loc();
983  else
984  unit_loc = src;
985 
986  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
987 
988  if( unit_it == resources::gameboard->units().end() ) {
989  std::ostringstream str;
990  str << "next_hop function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
991  throw formula_error( str.str(), "", "", 0);
992  }
993 
994  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
995 
996  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
997 
998  if( route.steps.size() < 2 ) {
999  return variant();
1000  }
1001 
1003  const ai::moves_map &possible_moves = ai_.get_possible_moves();
1004  const ai::moves_map::const_iterator& p_it = possible_moves.find(unit_loc);
1005  if (p_it==possible_moves.end() ) {
1006  return variant();
1007  }
1008 
1009  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
1010 
1011  if (p_it->second.destinations.find(*loc_iter) != p_it->second.destinations.end() ) {
1012  loc = *loc_iter;
1013  } else {
1014  break;
1015  }
1016  }
1017  if (loc==map_location::null_location()) {
1018  return variant();
1019  }
1020  return variant(std::make_shared<location_callable>(loc));
1021  }
1022 
1023  const formula_ai& ai_;
1024 };
1025 
1026 
1027 
1028 class move_function : public function_expression {
1029 public:
1030  explicit move_function(const args_list& args)
1031  : function_expression("move", args, 2, 2)
1032  {}
1033 private:
1034  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1035  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "move:src")).convert_to<location_callable>()->loc();
1036  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "move:dst")).convert_to<location_callable>()->loc();
1037  LOG_AI << "move(): " << src << ", " << dst << ")\n";
1038  return variant(std::make_shared<move_callable>(src, dst));
1039  }
1040 };
1041 
1042 
1043 class move_partial_function : public function_expression {
1044 public:
1045  explicit move_partial_function(const args_list& args)
1046  : function_expression("move_partial", args, 2, 2)
1047  {}
1048 private:
1049  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1050  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "move_partial:src")).convert_to<location_callable>()->loc();
1051  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "move_partial:dst")).convert_to<location_callable>()->loc();
1052  LOG_AI << "move_partial(): " << src << ", " << dst << ")\n";
1053  return variant(std::make_shared<move_partial_callable>(src, dst));
1054  }
1055 };
1056 
1057 
1058 class set_unit_var_function : public function_expression {
1059 public:
1060  explicit set_unit_var_function(const args_list& args)
1061  : function_expression("set_unit_var", args, 3, 3)
1062  {}
1063 private:
1064  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1065  return variant(std::make_shared<set_unit_var_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"set_unit_var:key")).as_string(), args()[1]->evaluate(variables,add_debug_info(fdb,1,"set_unit_var:value")), args()[2]->evaluate(variables,add_debug_info(fdb,2,"set_unit_var:unit_location")).convert_to<location_callable>()->loc()));
1066  }
1067 };
1068 
1069 
1070 class fallback_function : public function_expression {
1071 public:
1072  explicit fallback_function(const args_list& args)
1073  : function_expression("fallback", args, 0, 1)
1074  {}
1075 private:
1076  variant execute(const formula_callable& variables, formula_debugger*) const {
1077  // The parameter is not used, but is accepted for legacy compatibility
1078  if(args().size() == 1 && args()[0]->evaluate(variables).as_string() != "human")
1079  return variant();
1080  return variant(std::make_shared<fallback_callable>());
1081  }
1082 };
1083 
1084 
1085 class attack_function : public function_expression {
1086 public:
1087  explicit attack_function(const args_list& args)
1088  : function_expression("attack", args, 3, 4)
1089  {}
1090 private:
1091  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1092  const map_location move_from = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "attack:move_from")).convert_to<location_callable>()->loc();
1093  const map_location src = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "attack:src")).convert_to<location_callable>()->loc();
1094  const map_location dst = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "attack:dst")).convert_to<location_callable>()->loc();
1095  const int weapon = args().size() == 4 ? args()[3]->evaluate(variables,add_debug_info(fdb,3,"attack:weapon")).as_int() : -1;
1096  if(resources::gameboard->units().count(move_from) == 0 || resources::gameboard->units().count(dst) == 0) {
1097  ERR_AI << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst << std::endl;
1098  return variant();
1099  }
1100  return variant(std::make_shared<attack_callable>(move_from, src, dst, weapon));
1101  }
1102 };
1103 
1104 class debug_label_function : public function_expression {
1105 public:
1106  explicit debug_label_function(const args_list& args, const formula_ai& ai)
1107  : function_expression("debug_label", args, 2, 2),
1108  ai_(ai)
1109  {}
1110 private:
1111  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1112  const args_list& arguments = args();
1113  const variant var0 = arguments[0]->evaluate(variables,fdb);
1114  const variant var1 = arguments[1]->evaluate(variables,fdb);
1115 
1116  const map_location location = var0.convert_to<location_callable>()->loc();
1117  std::string text;
1118  if( var1.is_string() )
1119  text = var1.as_string();
1120  else
1121  text = var1.to_debug_string();
1122  display_label(location,text);
1123 
1124  std::vector<variant> res;
1125  res.push_back(var0);
1126  res.push_back(var1);
1127  return variant(res);
1128  }
1129 
1130  void display_label(const map_location& location, const std::string& text) const {
1132  std::string team_name;
1133 
1134  color_t color = team::get_side_rgb(ai_.get_side());
1135 
1136  const terrain_label *res;
1137  res = gui->labels().set_label(location, text, ai_.get_side() - 1, team_name, color);
1138  if (res && resources::recorder)
1140  }
1141 
1142  const formula_ai& ai_;
1143 };
1144 
1145 
1146 class is_village_function : public function_expression {
1147 public:
1148  explicit is_village_function(const args_list& args)
1149  : function_expression("is_village", args, 2, 3)
1150  {}
1151 private:
1152  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1153  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "is_village:map")).convert_to<gamemap_callable>()->get_gamemap();
1154 
1155  map_location loc;
1156  if(args().size() == 2) {
1157  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "is_village:location")).convert_to<location_callable>()->loc();
1158  } else {
1159  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_village:x")).as_int(),
1160  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_village:y")).as_int(), wml_loc());
1161  }
1162  return variant(m.is_village(loc));
1163  }
1164 };
1165 
1166 
1167 class is_unowned_village_function : public function_expression {
1168 public:
1169  explicit is_unowned_village_function(const args_list& args, const formula_ai& ai)
1170  : function_expression("is_unowned_village", args, 2, 3),
1171  ai_(ai)
1172  {}
1173 private:
1174  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1175 
1176  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "is_unowned_village:map")).convert_to<gamemap_callable>()->get_gamemap();
1177  const std::set<map_location>& my_villages = ai_.current_team().villages();
1178 
1179  map_location loc;
1180  if(args().size() == 2) {
1181  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "is_unowned_village:location")).convert_to<location_callable>()->loc();
1182  } else {
1183  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_unowned_village:x")).as_int(),
1184  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_unowned_village:y")).as_int(), wml_loc());
1185  }
1186 
1187  if(m.is_village(loc) && (my_villages.count(loc)==0) ) {
1188  return variant(true);
1189  } else {
1190  return variant(false);
1191  }
1192  }
1193 
1194  const formula_ai& ai_;
1195 };
1196 
1197 
1198 class unit_moves_function : public function_expression {
1199 public:
1200  unit_moves_function(const args_list& args, const formula_ai& ai_object)
1201  : function_expression("unit_moves", args, 1, 1), ai_(ai_object)
1202  {}
1203 private:
1204  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1205  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"unit_moves:unit_location"));
1206  std::vector<variant> vars;
1207  if(res.is_null()) {
1208  return variant(vars);
1209  }
1210 
1211  const map_location& loc = res.convert_to<location_callable>()->loc();
1212  const ai::move_map& srcdst = ai_.get_srcdst();
1213  typedef ai::move_map::const_iterator Itor;
1214  std::pair<Itor,Itor> range = srcdst.equal_range(loc);
1215 
1216  for(Itor i = range.first; i != range.second; ++i) {
1217  vars.emplace_back(std::make_shared<location_callable>(i->second));
1218  }
1219 
1220  return variant(vars);
1221  }
1222 
1223  const formula_ai& ai_;
1224 };
1225 
1226 
1227 class units_can_reach_function : public function_expression {
1228 public:
1229  units_can_reach_function(const args_list& args)
1230  : function_expression("units_can_reach", args, 2, 2)
1231  {}
1232 private:
1233  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1234  std::vector<variant> vars;
1235  variant dstsrc_var = args()[0]->evaluate(variables,add_debug_info(fdb,0,"units_can_reach:possible_move_list"));
1236  const ai::move_map& dstsrc = dstsrc_var.convert_to<move_map_callable>()->dstsrc();
1237  std::pair<ai::move_map::const_iterator,ai::move_map::const_iterator> range =
1238  dstsrc.equal_range(args()[1]->evaluate(variables, add_debug_info(fdb, 1, "units_can_reach:possible_move_list")).convert_to<location_callable>()->loc());
1239  while(range.first != range.second) {
1240  unit_map::const_iterator un = resources::gameboard->units().find(range.first->second);
1241  assert(un != resources::gameboard->units().end());
1242  vars.emplace_back(std::make_shared<unit_callable>(*un));
1243  ++range.first;
1244  }
1245 
1246  return variant(vars);
1247  }
1248 };
1249 
1250 class is_avoided_location_function : public function_expression {
1251 public:
1252  is_avoided_location_function(const args_list& args, const formula_ai& ai_object)
1253  : function_expression("is_avoided_location",args, 1, 1), ai_(ai_object)
1254  {}
1255 private:
1256  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1257  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"is_avoided_location:location"));
1258  if(res.is_null()) {
1259  return variant();
1260  }
1261  const map_location& loc = res.convert_to<location_callable>()->loc();
1262  return variant(ai_.get_avoid().match(loc));
1263  }
1264 
1265  const formula_ai &ai_;
1266 };
1267 
1268 class max_possible_damage_function : public function_expression {
1269 public:
1270  max_possible_damage_function(const args_list& args)
1271  : function_expression("max_possible_damage", args, 2, 2)
1272  {}
1273 private:
1274  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1275  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage:unit1"));
1276  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage:unit2"));
1277  if(u1.is_null() || u2.is_null()) {
1278  return variant();
1279  }
1280 
1281  unit_adapter u_attacker(u1), u_defender(u2);
1282  int best = 0;
1283  for(const attack_type& atk : u_attacker.attacks()) {
1284  const int dmg = round_damage(atk.damage(), u_defender.damage_from(atk), 100) * atk.num_attacks();
1285  if(dmg > best)
1286  best = dmg;
1287  }
1288  return variant(best);
1289  }
1290 };
1291 
1292 
1293 class max_possible_damage_with_retaliation_function : public function_expression {
1294 public:
1295  max_possible_damage_with_retaliation_function(const args_list& args)
1296  : function_expression("max_possible_damage_with_retaliation", args, 2, 2)
1297  {}
1298 private:
1299 
1300  std::pair<int, int> best_melee_and_ranged_attacks(unit_adapter attacker, unit_adapter defender) const {
1301  int highest_melee_damage = 0;
1302  int highest_ranged_damage = 0;
1303 
1304  for (const attack_type &attack : attacker.attacks()) {
1305  const int dmg = round_damage(attack.damage(), defender.damage_from(attack), 100) * attack.num_attacks();
1306  if (attack.range() == "melee") {
1307  highest_melee_damage = std::max(highest_melee_damage, dmg);
1308  } else {
1309  highest_ranged_damage = std::max(highest_ranged_damage, dmg);
1310  }
1311  }
1312 
1313  return std::make_pair(highest_melee_damage, highest_ranged_damage);
1314  }
1315 
1316  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1317  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage_with_retaliation:unit1"));
1318  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage_with_retaliation:unit2"));
1319 
1320  if(u1.is_null() || u2.is_null()) {
1321  return variant();
1322  }
1323 
1324  unit_adapter attacker(u1);
1325  unit_adapter defender(u2);
1326 
1327  // find max damage inflicted by attacker and by defender to the attacker
1328  std::pair<int, int> best_attacker_attacks = best_melee_and_ranged_attacks(attacker, defender);
1329  std::pair<int, int> best_defender_attacks = best_melee_and_ranged_attacks(defender, attacker);
1330 
1331  std::vector<variant> vars;
1332  vars.emplace_back(best_attacker_attacks.first);
1333  vars.emplace_back(best_attacker_attacks.second);
1334  vars.emplace_back(best_defender_attacks.first);
1335  vars.emplace_back(best_defender_attacks.second);
1336 
1337  return variant(vars);
1338  }
1339 };
1340 
1341 template<typename T>
1342 class ai_formula_function : public formula_function {
1343 protected:
1344  formula_ai& ai_;
1345 public:
1346  ai_formula_function(const std::string& name, ai::formula_ai& ai) : formula_function(name), ai_(ai) {}
1347  function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const {
1348  return function_expression_ptr(new T(args, ai_));
1349  }
1350 };
1351 
1352 }
1353 
1354 // This macro is for functions taking an additional formula_ai argument.
1355 // Functions using the other macro could potentially be made core.
1356 #define DECLARE_FAI_FUNCTION(name) add_function(#name, formula_function_ptr( \
1357  new ai_formula_function<name##_function>(#name, ai)))
1358 
1361 {
1362  function_symbol_table& functions_table = *this;
1363  DECLARE_WFL_FUNCTION(outcomes);
1364  //DECLARE_FAI_FUNCTION(evaluate_for_position);
1365  DECLARE_WFL_FUNCTION(move);
1366  DECLARE_WFL_FUNCTION(move_partial);
1367  DECLARE_WFL_FUNCTION(attack);
1368  DECLARE_FAI_FUNCTION(rate_action);
1369  DECLARE_WFL_FUNCTION(recall);
1370  DECLARE_WFL_FUNCTION(recruit);
1371  DECLARE_FAI_FUNCTION(is_avoided_location);
1372  DECLARE_WFL_FUNCTION(is_village);
1373  DECLARE_FAI_FUNCTION(is_unowned_village);
1375  DECLARE_WFL_FUNCTION(set_unit_var);
1376  DECLARE_WFL_FUNCTION(fallback);
1377  DECLARE_WFL_FUNCTION(units_can_reach);
1378  DECLARE_FAI_FUNCTION(debug_label);
1379  DECLARE_WFL_FUNCTION(max_possible_damage);
1380  DECLARE_WFL_FUNCTION(max_possible_damage_with_retaliation);
1381  DECLARE_FAI_FUNCTION(next_hop);
1382  DECLARE_WFL_FUNCTION(castle_locs);
1383  DECLARE_WFL_FUNCTION(timeofday_modifier);
1384  DECLARE_FAI_FUNCTION(distance_to_nearest_unowned_village);
1385  DECLARE_FAI_FUNCTION(shortest_path);
1386  DECLARE_FAI_FUNCTION(simplest_path);
1387  DECLARE_FAI_FUNCTION(nearest_keep);
1388  DECLARE_FAI_FUNCTION(suitable_keep);
1389  DECLARE_WFL_FUNCTION(nearest_loc);
1390  DECLARE_FAI_FUNCTION(find_shroud);
1391  DECLARE_FAI_FUNCTION(close_enemies);
1392  DECLARE_WFL_FUNCTION(calculate_outcome);
1393  DECLARE_FAI_FUNCTION(run_file);
1394  DECLARE_FAI_FUNCTION(calculate_map_ownership);
1395 }
1396 #undef DECLARE_WFL_FUNCTION
1397 
1398 }
Defines formula ai.
#define ERR_AI
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition: movetype.hpp:197
virtual const unit_map & units() const
Definition: game_board.hpp:97
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
unit_iterator end()
Definition: map.hpp:415
std::vector< char_t > string
size_t index(const utf8::string &str, const size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:71
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:88
const std::vector< node > & nodes
int movement_cost_
This class represents a single unit of a specific type.
Definition: unit.hpp:101
#define LOG_AI
size_t count(const map_location &loc) const
Definition: map.hpp:400
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:452
#define a
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:83
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:299
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
bool is_village(const map_location &loc) const
Definition: map.cpp:66
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:48
const movetype & movement_type() const
Definition: types.hpp:175
const_attack_itors attacks() const
Definition: types.cpp:492
General purpose widgets.
unit_iterator begin()
Definition: map.hpp:405
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:55
STL namespace.
Replay control code.
int wml_y() const
Definition: location.hpp:116
boost::iterator_range< boost::indirect_iterator< attack_list::const_iterator >> const_attack_itors
const std::vector< std::string > items
const unit * unit_
ai_function_symbol_table(ai::formula_ai &ai)
#define WRN_AI
A single unit type that the player may recruit.
Definition: types.hpp:43
const formula_ai & ai_
map_location loc_
#define b
unsigned in
If equal to search_counter, the node is off the list.
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:45
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
static color_t get_side_rgb(int side)
Definition: team.hpp:364
std::vector< map_location > steps
Definition: pathfind.hpp:134
int wml_x() const
Definition: location.hpp:115
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:131
bool valid() const
Definition: location.hpp:72
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition: label.cpp:146
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:382
int w() const
Effective map width.
Definition: map.hpp:90
game_board * gameboard
Definition: resources.cpp:20
std::shared_ptr< function_expression > function_expression_ptr
Definition: function.hpp:103
Encapsulates the map of the game.
Definition: map.hpp:34
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:170
int h
std::string path
Definition: game_config.cpp:56
map_display and display: classes which take care of displaying the map and game-data on the screen...
replay * recorder
Definition: resources.cpp:29
Function which only uses terrain, ignoring shroud, enemies, etc.
Definition: pathfind.hpp:240
const terrain_type & get_terrain_info(const t_translation::terrain_code &terrain) const
Definition: map.cpp:98
static const map_location & null_location()
Definition: location.hpp:220
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:133
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
Encapsulates the map of the game.
Definition: location.hpp:40
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)
int round_damage(int base_damage, int bonus, int divisor)
round (base_damage * bonus / divisor) to the closest integer, but up or down towards base_damage ...
Definition: math.hpp:57
size_t size(const utf8::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
static config unit_moves(reports::context &rc, const unit *u)
Definition: reports.cpp:581
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:211
int h() const
Effective map height.
Definition: map.hpp:93
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
static bool operator<(const placing_info &a, const placing_info &b)
Definition: game_state.cpp:132
#define DECLARE_FAI_FUNCTION(name)
Default AI contexts.
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:786
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition: unit.hpp:1225
#define i
Declarations for File-IO.
int w
static lg::log_domain log_formula_ai("ai/engine/fai")
#define DECLARE_WFL_FUNCTION(name)
Declares a function name in the local function table functions_table.
Definition: function.hpp:162
virtual const gamemap & map() const
Definition: game_board.hpp:96
#define next(ls)
Definition: llex.cpp:32
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition: unit.cpp:198
To store label data Class implements logic for rendering.
Definition: label.hpp:107
double t
Definition: astarsearch.cpp:64
Definition: contexts.hpp:42
bool find(E event, F functor)
Tests whether an event handler is available.
const unit_type * unit_type_
bool is_fearless() const
Gets whether this unit is fearless - ie, unaffected by time of day.
Definition: unit.hpp:1039
Standard logging facilities (interface).
unit_type::ALIGNMENT alignment() const
The alignment of this unit.
Definition: unit.hpp:371
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:70
Container associating units to locations.
Definition: map.hpp:99
static const char * name(const std::vector< SDL_Joystick * > &joysticks, const size_t index)
Definition: joystick.cpp:48
map_labels & labels()
Definition: display.cpp:2490
unit_iterator find(size_t id)
Definition: map.cpp:311
int damage_from(const attack_type &attack, bool attacker, const map_location &loc) const
Calculates the damage this unit would take from a certain attack.
Definition: unit.hpp:829
bool is_castle() const
Definition: terrain.hpp:63
const std::vector< map_location > & villages() const
Return a list of the locations of villages on the map.
Definition: map.hpp:157
static map_location::DIRECTION n
This module contains various pathfinding functions and utilities.
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
unit_map * units
Definition: resources.cpp:34
void add_label(const terrain_label *)
Definition: replay.cpp:262
int combat_modifier(const unit_map &units, const gamemap &map, const map_location &loc, unit_type::ALIGNMENT alignment, bool is_fearless)
Returns the amount that a unit's damage should be multiplied by due to the current time of day...
Definition: attack.cpp:1580
std::shared_ptr< formula > formula_ptr
Definition: formula_fwd.hpp:21