The Battle for Wesnoth  1.13.4+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 - 2016 by Bartosz Waresiak <[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 #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 
27 #include "attack_prediction.hpp"
28 #include "filesystem.hpp"
29 #include "game_board.hpp"
30 #include "display.hpp"
31 #include "log.hpp"
32 #include "map/label.hpp"
33 #include "pathfind/teleport.hpp"
34 #include "replay.hpp"
35 #include "resources.hpp"
36 #include "terrain/filter.hpp"
37 #include "units/unit.hpp"
38 #include "pathfind/pathfind.hpp"
39 
40 static lg::log_domain log_formula_ai("ai/engine/fai");
41 #define LOG_AI LOG_STREAM(info, log_formula_ai)
42 #define WRN_AI LOG_STREAM(warn, log_formula_ai)
43 #define ERR_AI LOG_STREAM(err, log_formula_ai)
44 
45 namespace game_logic {
46 using ai::formula_ai;
47 
48 namespace {
49 
50 /*
51  * unit adapters let us treat unit and unit_type the same if we want to get access to attacks or movement cost
52  */
53 class unit_adapter {
54  public:
55  unit_adapter(const variant& arg) : unit_type_(), unit_() {
56  const unit_callable* unit = try_convert_variant<unit_callable>(arg);
57 
58  if (unit) {
59  unit_ = &unit->get_unit();
60  } else {
61  unit_type_ = &(convert_variant<unit_type_callable>(arg)->get_unit_type());
62  }
63  }
64 
65  int damage_from(const attack_type& attack) const {
66  if(unit_type_ != nullptr) {
68  } else {
69  return unit_->damage_from(attack, false, map_location());
70  }
71  }
72 
73  // FIXME: we return a vector by value because unit_type and unit APIs
74  // disagree as to what should be returned by their attacks() method
75  std::vector<attack_type> attacks() const {
76  if(unit_type_ != nullptr) {
77  return unit_type_->attacks();
78  } else {
79  return unit_->attacks();
80  }
81  }
82 
83  int movement_cost(const t_translation::t_terrain & terrain) const {
84  if(unit_type_ != nullptr) {
85  return unit_type_->movement_type().movement_cost(terrain);
86  } else {
87  return unit_->movement_cost(terrain);
88  }
89  }
90 
91 
92  private:
94  const unit* unit_;
95 };
96 
97 
98 class distance_to_nearest_unowned_village_function : public function_expression {
99 public:
100  distance_to_nearest_unowned_village_function(const args_list& args, const formula_ai& ai)
101  : function_expression("distance_to_nearest_unowned_village", args, 1, 1), ai_(ai) {
102  }
103 
104 private:
105  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
106  const map_location loc = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"distance_to_nearest_unowned_village:location")))->loc();
107  int best = 1000000;
108  const std::vector<map_location>& villages = resources::gameboard->map().villages();
109  const std::set<map_location>& my_villages = ai_.current_team().villages();
110  for(std::vector<map_location>::const_iterator i = villages.begin(); i != villages.end(); ++i) {
111  int distance = distance_between(loc, *i);
112  if(distance < best) {
113  if(my_villages.count(*i) == 0) {
114  best = distance;
115  }
116  }
117  }
118 
119  return variant(best);
120  }
121 
122  const formula_ai& ai_;
123 };
124 
125 static unsigned search_counter;
126 
127 ///@todo 1.8: document
128 class calculate_map_ownership_function : public function_expression {
129 public:
130  calculate_map_ownership_function(const args_list& args, const formula_ai& ai)
131  : function_expression("calculate_map_ownership", args, 2, 5), ai_(ai) {
132  }
133 
134 private:
135 
136  struct indexer {
137  int w, h;
138  indexer(int a, int b) : w(a), h(b) { }
139  int operator()(const map_location& loc) const {
140  return loc.y * w + loc.x;
141  }
142  };
143 
144  struct node {
147 
148  /**
149  * If equal to search_counter, the node is off the list.
150  * If equal to search_counter + 1, the node is on the list.
151  * Otherwise it is outdated.
152  */
153  unsigned in;
154 
155  node(int moves, const map_location &loc)
156  : movement_cost_(moves)
157  , loc_(loc)
158  , in(0)
159  {
160  }
161 
162  node()
163  : movement_cost_(0)
164  , loc_()
165  , in(0)
166  {
167  }
168 
169  bool operator<(const node& o) const {
170  return movement_cost_ < o.movement_cost_;
171  }
172  };
173 
174  struct comp {
175  const std::vector<node>& nodes;
176  comp(const std::vector<node>& n) : nodes(n) { }
177  bool operator()(int l, int r) const {
178  return nodes[r] < nodes[l];
179  }
180  };
181 
182  void find_movemap(const unit_adapter& u, const map_location& loc,
183  std::vector<int>& scores, bool allow_teleport) const
184  {
185  const std::set<map_location>& teleports = allow_teleport ? ai_.current_team().villages() : std::set<map_location>();
186 
187  const gamemap& map = resources::gameboard->map();
188 
189  std::vector<map_location> locs(6 + teleports.size());
190  std::copy(teleports.begin(), teleports.end(), locs.begin() + 6);
191 
192  search_counter += 2;
193  if (search_counter == 0) search_counter = 2;
194 
195  static std::vector<node> nodes;
196  nodes.resize(map.w() * map.h());
197 
198  indexer index(map.w(), map.h());
199  comp node_comp(nodes);
200 
201  nodes[index(loc)] = node(0, loc);
202  std::vector<int> pq;
203  pq.push_back(index(loc));
204  while (!pq.empty()) {
205  node& n = nodes[pq.front()];
206  std::pop_heap(pq.begin(), pq.end(), node_comp);
207  pq.pop_back();
208  n.in = search_counter;
209 
210  get_adjacent_tiles(n.loc_, &locs[0]);
211  for (int i = teleports.count(n.loc_) ? locs.size() : 6; i-- > 0; ) {
212  if (!locs[i].valid(map.w(), map.h())) continue;
213 
214  node& next = nodes[index(locs[i])];
215  bool next_visited = next.in - search_counter <= 1u;
216 
217  // test if the current path to locs[i] is better than this one could possibly be.
218  // we do this a couple more times below
219  if (next_visited && !(n < next) ) continue;
220  const int move_cost = u.movement_cost(map[locs[i]]);
221 
222  node t = node(n.movement_cost_ + move_cost, locs[i]);
223 
224  if (next_visited && !(t < next) ) continue;
225 
226  bool in_list = next.in == search_counter + 1;
227  t.in = search_counter + 1;
228  next = t;
229 
230  // if already in the priority queue then we just update it, else push it.
231  if (in_list) {
232  std::push_heap(pq.begin(), std::find(pq.begin(), pq.end(), index(locs[i])) + 1, node_comp);
233  } else {
234  pq.push_back(index(locs[i]));
235  std::push_heap(pq.begin(), pq.end(), node_comp);
236  }
237  }
238  }
239 
240  for (int x = 0; x < map.w(); ++x) {
241  for (int y = 0; y < map.h(); ++y)
242  {
243  int i = y * map.w() + x;
244  const node &n = nodes[i];
245  scores[i] = scores[i] + n.movement_cost_;
246  //std::cout << x << "," << y << ":" << n.movement_cost << std::endl;
247  }
248  }
249  }
250 
251  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
252  int w = resources::gameboard->map().w();
253  int h = resources::gameboard->map().h();
254 
255  const variant units_input = args()[0]->evaluate(variables,fdb);
256  const variant leaders_input = args()[1]->evaluate(variables,fdb);
257 
258  int enemy_tollerancy = 3;
259  if( args().size() > 2 )
260  enemy_tollerancy = args()[2]->evaluate(variables,fdb).as_int();
261 
262  int enemy_border_tollerancy = 5;
263  if( args().size() > 3 )
264  enemy_border_tollerancy = args()[3]->evaluate(variables,fdb).as_int();
265 
266  int ally_tollerancy = 3;
267  if( args().size() > 4 )
268  ally_tollerancy = args()[4]->evaluate(variables,fdb).as_int();
269 
270  if( !units_input.is_list() )
271  return variant();
272 
273  size_t number_of_teams = units_input.num_elements();
274 
275  std::vector< std::vector<int> > scores( number_of_teams );
276 
277  for( size_t i = 0; i< number_of_teams; ++i)
278  scores[i].resize(w*h);
279 
280 // for(unit_map::const_iterator i = resources::units->begin(); i != resources::units->end(); ++i) {
281 // unit_counter[i->second.side()-1]++;
282 // unit_adapter unit(i->second);
283 // find_movemap( resources::gameboard->map(), *resources::units, unit, i->first, scores[i->second.side()-1], ai_.*resources::teams , true );
284 // }
285 
286  for(size_t side = 0 ; side < units_input.num_elements() ; ++side) {
287  if( leaders_input[side].is_empty() )
288  continue;
289 
290  const map_location loc = convert_variant<location_callable>(leaders_input[side][0])->loc();
291  const variant units_of_side = units_input[side];
292 
293  for(size_t unit_it = 0 ; unit_it < units_of_side.num_elements() ; ++unit_it) {
294  unit_adapter unit(units_of_side[unit_it]);
295  find_movemap( unit, loc, scores[side], true );
296  }
297  }
298 
299  size_t index = 0;
300  for( std::vector< std::vector<int> >::iterator i = scores.begin() ; i != scores.end() ; ++i) {
301  for( std::vector<int>::iterator j = i->begin() ; j != i->end() ; ++j ) {
302  if(units_input[index].num_elements() != 0) {
303  *j /= units_input[index].num_elements();
304  } else {
305  *j = 0;
306  }
307  }
308 
309  ++index;
310  }
311  //std::vector<variant> res;
312  std::map<variant, variant> res;
313 
314  size_t current_side = ai_.get_side() - 1 ;
315 
316  std::vector<int> enemies;
317  std::vector<int> allies;
318 
319  for(size_t side = 0 ; side < units_input.num_elements() ; ++side) {
320  if( side == current_side)
321  continue;
322 
323  if( ai_.current_team().is_enemy(side+1) ) {
324  if( !leaders_input[side].is_empty() )
325  enemies.push_back(side);
326  } else {
327  if( !leaders_input[side].is_empty() )
328  allies.push_back(side);
329  }
330  }
331 
332  //calculate_map_ownership( recruits_of_side, map(units_of_side, 'units', map( filter(units, leader), loc) ) )
333  //map(, debug_label(key,value))
334  for (int x = 0; x < w; ++x) {
335  for (int y = 0; y < h; ++y)
336  {
337  int i = y * w + x;
338  bool valid = true;
339  bool enemy_border = false;
340 
341  if( scores[current_side][i] > 98 )
342  continue;
343 
344  for (int side : enemies) {
345  int diff = scores[current_side][i] - scores[side][i];
346  if ( diff > enemy_tollerancy) {
347  valid = false;
348  break;
349  } else if( abs(diff) < enemy_border_tollerancy )
350  enemy_border = true;
351  }
352 
353  if( valid ) {
354  for (int side : allies) {
355  if ( scores[current_side][i] - scores[side][i] > ally_tollerancy ) {
356  valid = false;
357  break;
358  }
359  }
360  }
361 
362  if( valid ) {
363  if( enemy_border )
364  res.insert( std::pair<variant, variant>(variant(new location_callable(map_location(x, y))), variant(scores[0][i] + 10000) ));
365  else
366  res.insert( std::pair<variant, variant>(variant(new location_callable(map_location(x, y))), variant(scores[0][i] ) ));
367  }
368  }
369  }
370  return variant(&res);
371  }
372 
373  const formula_ai& ai_;
374 };
375 
376 
377 class nearest_loc_function : public function_expression {
378 public:
379  nearest_loc_function(const args_list& args)
380  : function_expression("nearest_loc", args, 2, 2)
381  {
382  }
383 
384 private:
385  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
386  const map_location loc = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"nearest_loc:location")))->loc();
387  variant items = args()[1]->evaluate(variables,add_debug_info(fdb,1,"nearest_loc:locations"));
388  int best = 1000000;
389  int best_i = -1;
390 
391  for(size_t i = 0; i < items.num_elements(); ++i) {
392 
393  const map_location move_loc = convert_variant<location_callable>(items[i])->loc();
394  int distance = distance_between(loc, move_loc);
395 
396  if(distance < best) {
397  best = distance;
398  best_i = i;
399  }
400  }
401 
402  if( best_i != -1)
403  return variant(new location_callable(convert_variant<location_callable>(items[best_i])->loc()));
404  else
405  return variant();
406  }
407 };
408 
409 
410 class adjacent_locs_function : public function_expression {
411 public:
412  adjacent_locs_function(const args_list& args)
413  : function_expression("adjacent_locs", args, 1, 1)
414  {
415  }
416 
417 private:
418  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
419  const map_location loc = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"adjacent_locs:location")))->loc();
420  map_location adj[6];
421  get_adjacent_tiles(loc, adj);
422 
423  std::vector<variant> v;
424  for(int n = 0; n != 6; ++n) {
425  if (resources::gameboard->map().on_board(adj[n]) )
426  v.push_back(variant(new location_callable(adj[n])));
427  }
428 
429  return variant(&v);
430  }
431 };
432 
433 
434 class locations_in_radius_function : public function_expression {
435 public:
436  locations_in_radius_function(const args_list& args)
437  : function_expression("locations_in_radius", args, 2, 2)
438  {
439  }
440 
441 private:
442  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
443  const map_location loc = convert_variant<location_callable>(args()[0]->evaluate(variables,fdb))->loc();
444 
445  int range = args()[1]->evaluate(variables,fdb).as_int();
446 
447  if( range < 0 )
448  return variant();
449 
450  if(!range)
451  return variant(new location_callable(loc));
452 
453  std::vector<map_location> res;
454 
455  get_tiles_in_radius( loc, range, res);
456 
457  std::vector<variant> v;
458  v.reserve(res.size()+1);
459  v.push_back(variant(new location_callable(loc)));
460 
461  for(size_t n = 0; n != res.size(); ++n) {
462  if (resources::gameboard->map().on_board(res[n]) )
463  v.push_back(variant(new location_callable(res[n])));
464  }
465 
466  return variant(&v);
467  }
468 };
469 
470 
471 /** FormulaAI function to run fai script from file. Usable from in-game console.
472 * arguments[0] - required file name, follows the usual wml convention
473 */
474 class run_file_function : public function_expression {
475 public:
476  explicit run_file_function(const args_list& args, formula_ai& ai)
477  : function_expression("run_file", args, 1, 1), ai_(ai)
478  {}
479 private:
480  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
481  const args_list& arguments = args();
482  const variant var0 = arguments[0]->evaluate(variables,add_debug_info(fdb,0,"run_file:file"));
483  const std::string filename = var0.string_cast();
484 
485  //NOTE: get_wml_location also filters file path to ensure it doesn't contain things like "../../top/secret"
487  if(path.empty()) {
488  ERR_AI << "run_file : not found [" << filename <<"]"<< std::endl;
489  return variant(); //no suitable file
490  }
491 
492  std::string formula_string = filesystem::read_file(path);
493  //need to get function_table from somewhere or delegate to someone who has access to it
494  formula_ptr parsed_formula = ai_.create_optional_formula(formula_string);
495  if(parsed_formula == game_logic::formula_ptr()) {
496  ERR_AI << "run_file : unable to create formula"<< std::endl;
497  return variant(); //was unable to create a formula from file
498  }
499  return parsed_formula->evaluate(variables,add_debug_info(fdb,-1,"run_file:formula_from_file"));
500  }
501 
502  formula_ai& ai_;
503 };
504 
505 
506 class castle_locs_function : public function_expression {
507 public:
508  castle_locs_function(const args_list& args)
509  : function_expression("castle_locs", args, 1, 1)
510  {
511  }
512 
513 private:
514  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
515  const map_location starting_loc = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"castle_locs:location")))->loc();
516 
517  //looks like reimplementing a generic graph search algorithm to me
518  std::set< map_location > visited_locs;
519  std::queue< map_location > queued_locs;
520 
521  queued_locs.push(starting_loc);
522 
523  while( !queued_locs.empty() ) {
524  const map_location loc = queued_locs.front();
525  queued_locs.pop();
526 
527  if ( visited_locs.find( loc ) != visited_locs.end() )
528  continue;
529 
530  visited_locs.insert(loc);
531 
532  map_location adj[6];
533  get_adjacent_tiles(loc, adj);
534 
535  for(int n = 0; n != 6; ++n) {
536  if (resources::gameboard->map().on_board(adj[n]) && visited_locs.find( adj[n] ) == visited_locs.end() ) {
537  if (resources::gameboard->map().get_terrain_info(adj[n]).is_keep() ||
539  queued_locs.push(adj[n]);
540  }
541  }
542  }
543  }
544 
545  if ( !resources::gameboard->map().get_terrain_info(starting_loc).is_keep() &&
546  !resources::gameboard->map().get_terrain_info(starting_loc).is_castle() )
547  visited_locs.erase(starting_loc);
548 
549  std::vector<variant> res;
550  for (const map_location& ml : visited_locs) {
551  res.push_back( variant(new location_callable( ml ) ) );
552  }
553 
554  return variant(&res);
555  }
556 };
557 
558 
559 /**
560  * timeofday_modifer formula function. Returns combat modifier, taking
561  * alignment, illuminate, time of day and fearless trait into account.
562  * 'leadership' and 'slowed' are not taken into account.
563  * arguments[0] - unit
564  * arguments[1] - location (optional, defaults to unit's current location.
565  */
566 class timeofday_modifier_function : public function_expression {
567 public:
568  timeofday_modifier_function(const args_list& args)
569  : function_expression("timeofday_modifier", args, 1, 2)
570  {
571  }
572 private:
573  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
574  variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"timeofday_modifier:unit"));
575 
576  if( u.is_null() ) {
577  return variant();
578  }
579 
580  const unit_callable* u_call = try_convert_variant<unit_callable>(u);
581 
582  if (u_call == nullptr) {
583  return variant();
584  }
585 
586  const unit& un = u_call->get_unit();
587 
588  map_location const* loc = nullptr;
589 
590  if(args().size()==2) {
591  loc = &convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"timeofday_modifier:location")))->loc();
592  }
593 
594  if (loc == nullptr) {
595  loc = &u_call->get_location();
596  }
597 
599  }
600 };
601 
602 
603 class nearest_keep_function : public function_expression {
604 public:
605  nearest_keep_function(const args_list& args, const formula_ai& ai)
606  : function_expression("nearest_keep", args, 1, 1), ai_(ai) {
607  }
608 
609 private:
610  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
611  const map_location loc = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"nearest_keep:location")))->loc();
612  int best = 1000000;
613  int best_i = -1;
614 
615  ai_.get_keeps();
616  int size = ai_.get_keeps_cache().num_elements();
617 
618  for( int i = 0 ; i < size; ++i) {
619  int distance = distance_between(loc, convert_variant<location_callable>(ai_.get_keeps_cache()[i])->loc() );
620  if(distance < best)
621  {
622  best = distance;
623  best_i = i;
624  }
625  }
626 
627  if( best_i != -1)
628  return variant(new location_callable(convert_variant<location_callable>(ai_.get_keeps_cache()[best_i])->loc()));
629  else
630  return variant();
631  }
632 
633  const formula_ai& ai_;
634 };
635 
636 
637 /**
638 * Find suitable keep for unit at location
639 * arguments[0] - location for unit on which the suitable keep is to be found
640 */
641 class suitable_keep_function : public function_expression {
642 public:
643  suitable_keep_function(const args_list& args, formula_ai& ai)
644  : function_expression("suitable_keep", args, 1, 1), ai_(ai) {
645  }
646 
647 private:
648  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
649  const map_location loc = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"suitable_keep:location")))->loc();
650  const unit_map& units = *resources::units;
651  const unit_map::const_iterator u = units.find(loc);
652  if (u == units.end()){
653  return variant();
654  }
655  const pathfind::paths unit_paths(*u, false, true, ai_.current_team());
656  return variant(new location_callable(ai_.suitable_keep(loc,unit_paths)));
657  }
658 
659  formula_ai& ai_;
660 };
661 
662 
663 class find_shroud_function : public function_expression {
664 public:
665  find_shroud_function(const args_list& args, const formula_ai& ai)
666  : function_expression("find_shroud", args, 0, 1), ai_(ai) {
667  }
668 
669 private:
670  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
671  std::vector<variant> vars;
672  int w,h;
673 
674  if(args().size()==1) {
675  const gamemap& m = convert_variant<gamemap_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"find_shroud:gamemap")))->get_gamemap();
676  w = m.w();
677  h = m.h();
678  } else {
679  w = resources::gameboard->map().w();
680  h = resources::gameboard->map().h();
681  }
682 
683  for(int i = 0; i < w; ++i)
684  for(int j = 0; j < h; ++j) {
685  if(ai_.current_team().shrouded(map_location(i,j)))
686  vars.push_back(variant(new location_callable(i,j)));
687  }
688 
689  return variant(&vars);
690  }
691 
692  const formula_ai& ai_;
693 };
694 
695 
696 class close_enemies_function : public function_expression {
697 public:
698  close_enemies_function(const args_list& args, const formula_ai& ai)
699  : function_expression("close_enemies", args, 2, 2), ai_(ai) {
700  }
701 
702 private:
703  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
704  std::vector<variant> vars;
705  const map_location loc = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"close_enemies:location")))->loc();
706  int range_s = args()[1]->evaluate(variables,add_debug_info(fdb,1,"close_enemies:distance")).as_int();
707  if (range_s < 0) {
708  WRN_AI << "close_enemies_function: range is negative (" << range_s << ")" << std::endl;
709  range_s = 0;
710  }
711  size_t range = static_cast<size_t>(range_s);
714  while (un != end) {
715  if (distance_between(loc, un->get_location()) <= range) {
716  if (un->side() != ai_.get_side()) {//fixme: ignores allied units
717  vars.push_back(variant(new unit_callable(*un)));
718  }
719  }
720  ++un;
721  }
722  return variant(&vars);
723  }
724 
725  const formula_ai& ai_;
726 };
727 
728 class calculate_outcome_function : public function_expression {
729 public:
730  calculate_outcome_function(const args_list& args)
731  : function_expression( "calculate_outcome", args, 3, 4)
732  {
733  }
734 
735 private:
736  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
737  std::vector<variant> vars;
738  int weapon;
739  if (args().size() > 3) weapon = args()[3]->evaluate(variables,add_debug_info(fdb,3,"calculate_outcome:weapon")).as_int();
740  else weapon = -1;
741 
742  const unit_map& units = *resources::units;
743  map_location attacker_location =
744  convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"calculate_outcome:attacker_current_location")))->loc();
745  if(units.count(attacker_location) == 0) {
746  ERR_AI << "Performing calculate_outcome() with non-existent attacker at (" <<
747  attacker_location.x+1 << "," << attacker_location.y+1 << ")\n";
748  return variant();
749  }
750 
751  map_location defender_location =
752  convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"calculate_outcome:defender_location")))->loc();
753  if(units.count(defender_location) == 0) {
754  ERR_AI << "Performing calculate_outcome() with non-existent defender at (" <<
755  defender_location.x+1 << "," << defender_location.y+1 << ")\n";
756  return variant();
757  }
758 
759  battle_context bc(units, convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"calculate_outcome:attacker_attack_location")))->loc(),
760  defender_location, weapon, -1, 1.0, nullptr, &*units.find(attacker_location));
761  std::vector<double> hp_dist = bc.get_attacker_combatant().hp_dist;
762  std::vector<double>::iterator it = hp_dist.begin();
763  int i = 0;
764  std::vector<variant> hitLeft;
765  std::vector<variant> prob;
766  while (it != hp_dist.end()) {
767  if (*it != 0) {
768  hitLeft.push_back(variant(i));
769  prob.push_back(variant(int(*it*10000)));
770  }
771  ++it;
772  ++i;
773  }
774  std::vector<variant> status;
775  if (bc.get_attacker_combatant().poisoned != 0)
776  status.push_back(variant("Poisoned"));
777  if (bc.get_attacker_combatant().slowed != 0)
778  status.push_back(variant("Slowed"));
779  if (bc.get_defender_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_attacker_stats().hp)
780  status.push_back(variant("Stoned"));
781  if (bc.get_defender_stats().plagues && hitLeft[0].as_int() == 0)
782  status.push_back(variant("Zombiefied"));
783  vars.push_back(variant(new outcome_callable(hitLeft, prob, status)));
784  hitLeft.clear();
785  prob.clear();
786  status.clear();
787  hp_dist = bc.get_defender_combatant().hp_dist;
788  it = hp_dist.begin();
789  i = 0;
790  while (it != hp_dist.end()) {
791  if (*it != 0) {
792  hitLeft.push_back(variant(i));
793  prob.push_back(variant(int(*it*10000)));
794  }
795  ++it;
796  ++i;
797  }
798  if (bc.get_defender_combatant().poisoned != 0)
799  status.push_back(variant("Poisoned"));
800  if (bc.get_defender_combatant().slowed != 0)
801  status.push_back(variant("Slowed"));
802  if (bc.get_attacker_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_defender_stats().hp)
803  status.push_back(variant("Stoned"));
804  if (bc.get_attacker_stats().plagues && hitLeft[0].as_int() == 0)
805  status.push_back(variant("Zombiefied"));
806  vars.push_back(variant(new outcome_callable(hitLeft, prob, status)));
807  return variant(&vars);
808  }
809 };
810 
811 
812 class outcomes_function : public function_expression {
813 public:
814  outcomes_function(const args_list& args)
815  : function_expression("outcomes", args, 1, 1)
816  {
817  }
818 
819 private:
820  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
821  variant attack = args()[0]->evaluate(variables,add_debug_info(fdb,0,"outcomes:attack"));
822  ai::attack_analysis* analysis = convert_variant<ai::attack_analysis>(attack);
823  //unit_map units_with_moves(*resources::units);
824  //typedef std::pair<map_location, map_location> mv;
825  //for(const mv &m : analysis->movements) {
826  // units_with_moves.move(m.first, m.second);
827  //}
828 
829  std::vector<variant> vars;
830  if(analysis->chance_to_kill > 0.0) {
831  //unit_map units(units_with_moves);
832  //units.erase(analysis->target);
833  vars.push_back(variant(new position_callable(/*&units,*/ static_cast<int>(analysis->chance_to_kill*100))));
834 
835  }
836 
837  if(analysis->chance_to_kill < 1.0) {
838  //unit_map units(units_with_moves);
839  vars.push_back(variant(new position_callable(/*&units,*/ static_cast<int>(100 - analysis->chance_to_kill*100))));
840  }
841 
842  return variant(&vars);
843  }
844 };
845 
846 //class evaluate_for_position_function : public function_expression {
847 //public:
848 // evaluate_for_position_function(const args_list& args, formula_ai& ai)
849 // : function_expression("evaluate_for_position", args, 2, 2), ai_(ai) {
850 // }
851 //
852 //private:
853 // variant execute(const formula_callable& variables, formula_debugger *fdb) const {
854 // variant position = args()[0]->evaluate(variables,add_debug_info(fdb,0,"evaluate_for_position:position"));
855 // ai_.store_outcome_position(position);
856 // position_callable* pos = convert_variant<position_callable>(position);
857 // position_callable::swapper swapper(ai_, *pos);
858 // return args()[1]->evaluate(variables,add_debug_info(fdb,1,"evaluate_for_position:formula"));
859 // }
860 //
861 // formula_ai& ai_;
862 //};
863 
864 class get_unit_type_function : public function_expression {
865 public:
866  explicit get_unit_type_function(const args_list& args)
867  : function_expression("get_unit_type", args, 1, 1)
868  {}
869 private:
870  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
871  const std::string type = args()[0]->evaluate(variables,add_debug_info(fdb,0,"get_unit_type:name")).as_string();
872 
873  const unit_type *ut = unit_types.find(type);
874  if(ut) {
875  return variant(new unit_type_callable(*ut));
876  }
877 
878  return variant();
879  }
880 };
881 
882 
883 
884 class rate_action_function : public function_expression {
885 public:
886  explicit rate_action_function(const args_list& args, const formula_ai &ai)
887  : function_expression("rate_action", args, 1, 1), ai_(ai)
888  {}
889 private:
890  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
891  variant act = args()[0]->evaluate(variables,add_debug_info(fdb,0,"rate_action:action"));
892  ai::attack_analysis* analysis = convert_variant<ai::attack_analysis>(act);
893 
894  return variant(analysis->rating(ai_.get_aggression(),ai_)*1000,variant::DECIMAL_VARIANT);
895  }
896  const formula_ai &ai_;
897 };
898 
899 
900 
901 class recall_function : public function_expression {
902 public:
903  explicit recall_function(const args_list& args)
904  : function_expression("recall", args, 1, 2)
905  {}
906 private:
907  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
908  const std::string id = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recall:id")).as_string();
909  map_location loc;
910  if(args().size() >= 2) {
911  loc = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"recall:location")))->loc();
912  }
913 
914  return variant(new recall_callable(loc, id));
915  }
916 };
917 
918 
919 class recruit_function : public function_expression {
920 public:
921  explicit recruit_function(const args_list& args)
922  : function_expression("recruit", args, 1, 2)
923  {}
924 private:
925  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
926  const std::string type = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recruit:type")).as_string();
927  map_location loc;
928  if(args().size() >= 2) {
929  loc = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"recruit:location")))->loc();
930  }
931 
932  return variant(new recruit_callable(loc, type));
933  }
934 };
935 
936 
937 class shortest_path_function : public function_expression {
938 public:
939  explicit shortest_path_function(const args_list& args, const formula_ai& ai)
940  : function_expression("shortest_path", args, 2, 3), ai_(ai)
941  {}
942 
943 private:
944  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
945 
946  std::vector<variant> locations;
947 
948  const map_location src = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"shortest_path:src")))->loc();
949  const map_location dst = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"shortest_path:dst")))->loc();
950  map_location unit_loc;
951 
952  if( src == dst )
953  return variant(&locations);
954 
955  if(args().size() > 2)
956  unit_loc = convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"shortest_path:unit_location")))->loc();
957  else
958  unit_loc = src;
959 
960  unit_map::iterator unit_it = resources::units->find(unit_loc);
961 
962  if( unit_it == resources::units->end() ) {
963  std::ostringstream str;
964  str << "shortest_path function: expected unit at location (" << (unit_loc.x+1) << "," << (unit_loc.y+1) << ")";
965  throw formula_error( str.str(), "", "", 0);
966  }
967 
968  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
969 
970  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
971 
972  if( route.steps.size() < 2 ) {
973  return variant(&locations);
974  }
975 
976  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
977  locations.push_back( variant( new location_callable(*loc_iter) ));
978  }
979 
980  return variant(&locations);
981  }
982 
983  const formula_ai& ai_;
984 };
985 
986 
987 class simplest_path_function : public function_expression {
988 public:
989  explicit simplest_path_function(const args_list& args, const formula_ai& ai)
990  : function_expression("simplest_path", args, 2, 3), ai_(ai)
991  {}
992 
993 private:
994  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
995 
996  std::vector<variant> locations;
997 
998  const map_location src = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"simplest_path:src")))->loc();
999  const map_location dst = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"simplest_path:dst")))->loc();
1000  map_location unit_loc;
1001 
1002  if( src == dst )
1003  return variant(&locations);
1004 
1005  if(args().size() > 2)
1006  unit_loc = convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"simplest_path:unit_location")))->loc();
1007  else
1008  unit_loc = src;
1009 
1010  unit_map::iterator unit_it = resources::units->find(unit_loc);
1011 
1012  if( unit_it == resources::units->end() ) {
1013  std::ostringstream str;
1014  str << "simplest_path function: expected unit at location (" << (unit_loc.x+1) << "," << (unit_loc.y+1) << ")";
1015  throw formula_error( str.str(), "", "", 0);
1016  }
1017 
1018  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
1019 
1021 
1022  pathfind::plain_route route = pathfind::a_star_search(src, dst, 1000.0, &em_calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
1023 
1024  if( route.steps.size() < 2 ) {
1025  return variant(&locations);
1026  }
1027 
1028  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
1029  if (unit_it->movement_cost((resources::gameboard->map())[*loc_iter]) < movetype::UNREACHABLE )
1030  locations.push_back( variant( new location_callable(*loc_iter) ));
1031  else
1032  break;
1033  }
1034 
1035  return variant(&locations);
1036  }
1037 
1038  const formula_ai& ai_;
1039 };
1040 
1041 
1042 
1043 class next_hop_function : public function_expression {
1044 public:
1045  explicit next_hop_function(const args_list& args, const formula_ai& ai)
1046  : function_expression("next_hop", args, 2, 3), ai_(ai)
1047  {}
1048 
1049 private:
1050  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1051 
1052  const map_location src = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"next_hop:src")))->loc();
1053  const map_location dst = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"next_hop:dst")))->loc();
1054  map_location unit_loc;
1055 
1056  if( src == dst )
1057  return variant();
1058 
1059  if(args().size() > 2)
1060  unit_loc = convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"next_hop:unit_location")))->loc();
1061  else
1062  unit_loc = src;
1063 
1064  unit_map::iterator unit_it = resources::units->find(unit_loc);
1065 
1066  if( unit_it == resources::units->end() ) {
1067  std::ostringstream str;
1068  str << "next_hop function: expected unit at location (" << (unit_loc.x+1) << "," << (unit_loc.y+1) << ")";
1069  throw formula_error( str.str(), "", "", 0);
1070  }
1071 
1072  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
1073 
1074  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
1075 
1076  if( route.steps.size() < 2 ) {
1077  return variant();
1078  }
1079 
1081  const ai::moves_map &possible_moves = ai_.get_possible_moves();
1082  const ai::moves_map::const_iterator& p_it = possible_moves.find(unit_loc);
1083  if (p_it==possible_moves.end() ) {
1084  return variant();
1085  }
1086 
1087  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
1088 
1089  if (p_it->second.destinations.find(*loc_iter) != p_it->second.destinations.end() ) {
1090  loc = *loc_iter;
1091  } else {
1092  break;
1093  }
1094  }
1095  if (loc==map_location::null_location()) {
1096  return variant();
1097  }
1098  return variant(new location_callable(loc));
1099  }
1100 
1101  const formula_ai& ai_;
1102 };
1103 
1104 
1105 
1106 class move_function : public function_expression {
1107 public:
1108  explicit move_function(const args_list& args)
1109  : function_expression("move", args, 2, 2)
1110  {}
1111 private:
1112  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1113  const map_location src = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"move:src")))->loc();
1114  const map_location dst = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"move:dst")))->loc();
1115  LOG_AI << "move(): " << src << ", " << dst << ")\n";
1116  return variant(new move_callable(src, dst));
1117  }
1118 };
1119 
1120 
1121 class move_partial_function : public function_expression {
1122 public:
1123  explicit move_partial_function(const args_list& args)
1124  : function_expression("move_partial", args, 2, 2)
1125  {}
1126 private:
1127  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1128  const map_location src = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"move_partial:src")))->loc();
1129  const map_location dst = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"move_partial:dst")))->loc();
1130  LOG_AI << "move_partial(): " << src << ", " << dst << ")\n";
1131  return variant(new move_partial_callable(src, dst));
1132  }
1133 };
1134 
1135 
1136 class set_var_function : public function_expression {
1137 public:
1138  explicit set_var_function(const args_list& args)
1139  : function_expression("set_var", args, 2, 2)
1140  {}
1141 private:
1142  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1143  return variant(new set_var_callable(args()[0]->evaluate(variables,add_debug_info(fdb,0,"set_var:key")).as_string(), args()[1]->evaluate(variables,add_debug_info(fdb,1,"set_var:value"))));
1144  }
1145 };
1146 
1147 
1148 class set_unit_var_function : public function_expression {
1149 public:
1150  explicit set_unit_var_function(const args_list& args)
1151  : function_expression("set_unit_var", args, 3, 3)
1152  {}
1153 private:
1154  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1155  return variant(new 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")), convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"set_unit_var:unit_location")))->loc()));
1156  }
1157 };
1158 
1159 
1160 class fallback_function : public function_expression {
1161 public:
1162  explicit fallback_function(const args_list& args)
1163  : function_expression("fallback", args, 0, 1)
1164  {}
1165 private:
1166  variant execute(const formula_callable& variables, formula_debugger*) const {
1167  // The parameter is not used, but is accepted for legacy compatibility
1168  if(args().size() == 1 && args()[0]->evaluate(variables).as_string() != "human")
1169  return variant();
1170  return variant(new fallback_callable);
1171  }
1172 };
1173 
1174 
1175 class attack_function : public function_expression {
1176 public:
1177  explicit attack_function(const args_list& args)
1178  : function_expression("attack", args, 3, 4)
1179  {}
1180 private:
1181  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1182  const map_location move_from = convert_variant<location_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"attack:move_from")))->loc();
1183  const map_location src = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"attack:src")))->loc();
1184  const map_location dst = convert_variant<location_callable>(args()[2]->evaluate(variables,add_debug_info(fdb,2,"attack:dst")))->loc();
1185  const int weapon = args().size() == 4 ? args()[3]->evaluate(variables,add_debug_info(fdb,3,"attack:weapon")).as_int() : -1;
1186  if(resources::units->count(move_from) == 0 || resources::units->count(dst) == 0) {
1187  ERR_AI << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst << std::endl;
1188  return variant();
1189  }
1190  return variant(new attack_callable(move_from, src, dst, weapon));
1191  }
1192 };
1193 
1194 
1195 class safe_call_function : public function_expression {
1196 public:
1197  explicit safe_call_function(const args_list& args)
1198  : function_expression("safe_call", args, 2, 2)
1199  {}
1200 private:
1201  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1202  const variant main = args()[0]->evaluate(variables,fdb);
1203  const expression_ptr backup_formula = args()[1];
1204 
1205  return variant(new safe_call_callable(main, backup_formula));
1206  }
1207 };
1208 
1209 class debug_label_function : public function_expression {
1210 public:
1211  explicit debug_label_function(const args_list& args, const formula_ai& ai)
1212  : function_expression("debug_label", args, 2, 2),
1213  ai_(ai)
1214  {}
1215 private:
1216  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1217  const args_list& arguments = args();
1218  const variant var0 = arguments[0]->evaluate(variables,fdb);
1219  const variant var1 = arguments[1]->evaluate(variables,fdb);
1220 
1221  const map_location location = convert_variant<location_callable>(var0)->loc();
1222  std::string text;
1223  if( var1.is_string() )
1224  text = var1.as_string();
1225  else
1226  text = var1.to_debug_string();
1227  display_label(location,text);
1228 
1229  std::vector<variant> res;
1230  res.push_back(var0);
1231  res.push_back(var1);
1232  return variant(&res);
1233  }
1234 
1235  void display_label(const map_location& location, const std::string& text) const {
1237  std::string team_name;
1238 
1239  SDL_Color color = int_to_color(team::get_side_rgb(ai_.get_side()));
1240 
1241  const terrain_label *res;
1242  res = gui->labels().set_label(location, text, ai_.get_side() - 1, team_name, color);
1243  if (res && resources::recorder)
1245  }
1246 
1247  const formula_ai& ai_;
1248 };
1249 
1250 
1251 class is_village_function : public function_expression {
1252 public:
1253  explicit is_village_function(const args_list& args)
1254  : function_expression("is_village", args, 2, 3)
1255  {}
1256 private:
1257  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1258  const gamemap& m = convert_variant<gamemap_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"is_village:map")))->get_gamemap();
1259 
1260  map_location loc;
1261  if(args().size() == 2) {
1262  loc = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_village:location")))->loc();
1263  } else {
1264  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_village:x")).as_int() - 1,
1265  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_village:y")).as_int() - 1 );
1266  }
1267  return variant(m.is_village(loc));
1268  }
1269 };
1270 
1271 
1272 class is_unowned_village_function : public function_expression {
1273 public:
1274  explicit is_unowned_village_function(const args_list& args, const formula_ai& ai)
1275  : function_expression("is_unowned_village", args, 2, 3),
1276  ai_(ai)
1277  {}
1278 private:
1279  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1280 
1281  const gamemap& m = convert_variant<gamemap_callable>(args()[0]->evaluate(variables,add_debug_info(fdb,0,"is_unowned_village:map")))->get_gamemap();
1282  const std::set<map_location>& my_villages = ai_.current_team().villages();
1283 
1284  map_location loc;
1285  if(args().size() == 2) {
1286  loc = convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_unowned_village:location")))->loc();
1287  } else {
1288  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_unowned_village:x")).as_int() - 1,
1289  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_unowned_village:y")).as_int() - 1 );
1290  }
1291 
1292  if(m.is_village(loc) && (my_villages.count(loc)==0) ) {
1293  return variant(true);
1294  } else {
1295  return variant(false);
1296  }
1297  }
1298 
1299  const formula_ai& ai_;
1300 };
1301 
1302 
1303 class unit_at_function : public function_expression {
1304 public:
1305  unit_at_function(const args_list& args)
1306  : function_expression("unit_at", args, 1, 1)
1307  {}
1308 private:
1309  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1310  variant loc_var = args()[0]->evaluate(variables,add_debug_info(fdb,0,"unit_at:location"));
1311  if (loc_var.is_null()) {
1312  return variant();
1313  }
1314  const location_callable* loc = convert_variant<location_callable>(loc_var);
1316  if(i != resources::units->end()) {
1317  return variant(new unit_callable(*i));
1318  } else {
1319  return variant();
1320  }
1321  }
1322 };
1323 
1324 
1325 class unit_moves_function : public function_expression {
1326 public:
1327  unit_moves_function(const args_list& args, const formula_ai& ai_object)
1328  : function_expression("unit_moves", args, 1, 1), ai_(ai_object)
1329  {}
1330 private:
1331  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1332  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"unit_moves:unit_location"));
1333  std::vector<variant> vars;
1334  if(res.is_null()) {
1335  return variant(&vars);
1336  }
1337 
1338  const map_location& loc = convert_variant<location_callable>(res)->loc();
1339  const ai::move_map& srcdst = ai_.get_srcdst();
1340  typedef ai::move_map::const_iterator Itor;
1341  std::pair<Itor,Itor> range = srcdst.equal_range(loc);
1342 
1343  for(Itor i = range.first; i != range.second; ++i) {
1344  vars.push_back(variant(new location_callable(i->second)));
1345  }
1346 
1347  return variant(&vars);
1348  }
1349 
1350  const formula_ai& ai_;
1351 };
1352 
1353 
1354 class units_can_reach_function : public function_expression {
1355 public:
1356  units_can_reach_function(const args_list& args)
1357  : function_expression("units_can_reach", args, 2, 2)
1358  {}
1359 private:
1360  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1361  std::vector<variant> vars;
1362  variant dstsrc_var = args()[0]->evaluate(variables,add_debug_info(fdb,0,"units_can_reach:possible_move_list"));
1363  const ai::move_map& dstsrc = convert_variant<move_map_callable>(dstsrc_var)->dstsrc();
1364  std::pair<ai::move_map::const_iterator,ai::move_map::const_iterator> range =
1365  dstsrc.equal_range(convert_variant<location_callable>(args()[1]->evaluate(variables,add_debug_info(fdb,1,"units_can_reach:possible_move_list")))->loc());
1366  while(range.first != range.second) {
1367  unit_map::const_iterator un = resources::units->find(range.first->second);
1368  assert(un != resources::units->end());
1369  vars.push_back(variant(new unit_callable(*un)));
1370  ++range.first;
1371  }
1372 
1373  return variant(&vars);
1374  }
1375 };
1376 
1377 
1378 class defense_on_function : public function_expression {
1379 public:
1380  defense_on_function(const args_list& args)
1381  : function_expression("defense_on", args, 2, 2)
1382  {}
1383 private:
1384  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1385  variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"defense_on:unit"));
1386  variant loc_var = args()[1]->evaluate(variables,add_debug_info(fdb,1,"defense_on:location"));
1387  if(u.is_null() || loc_var.is_null()) {
1388  return variant();
1389  }
1390 
1391  const unit_callable* u_call = try_convert_variant<unit_callable>(u);
1392  const unit_type_callable* u_type = try_convert_variant<unit_type_callable>(u);
1393  const map_location& loc = convert_variant<location_callable>(loc_var)->loc();
1394 
1395  if (u_call)
1396  {
1397  const unit& un = u_call->get_unit();
1398 
1399  if( un.total_movement() < un.movement_cost( (resources::gameboard->map())[loc]) )
1400  return variant();
1401 
1402  if(!resources::gameboard->map().on_board(loc)) {
1403  return variant();
1404  }
1405 
1406  return variant(100 - un.defense_modifier((resources::gameboard->map())[loc]));
1407  }
1408 
1409  if (u_type)
1410  {
1411  const unit_type& un = u_type->get_unit_type();
1412 
1413  if( un.movement() < un.movement_type().movement_cost((resources::gameboard->map())[loc]) )
1414  return variant();
1415 
1416  if(!resources::gameboard->map().on_board(loc)) {
1417  return variant();
1418  }
1419 
1420  return variant(100 - un.movement_type().defense_modifier((resources::gameboard->map())[loc]));
1421  }
1422 
1423  return variant();
1424  }
1425 };
1426 
1427 
1428 class chance_to_hit_function : public function_expression {
1429 public:
1430  chance_to_hit_function(const args_list& args)
1431  : function_expression("chance_to_hit", args, 2, 2)
1432  {}
1433 private:
1434  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1435  variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"chance_to_hit:unit"));
1436  variant loc_var = args()[1]->evaluate(variables,add_debug_info(fdb,1,"chance_to_hit:location"));
1437  if(u.is_null() || loc_var.is_null()) {
1438  return variant();
1439  }
1440 
1441  const unit_callable* u_call = try_convert_variant<unit_callable>(u);
1442  const unit_type_callable* u_type = try_convert_variant<unit_type_callable>(u);
1443  const map_location& loc = convert_variant<location_callable>(loc_var)->loc();
1444 
1445  if (u_call)
1446  {
1447  const unit& un = u_call->get_unit();
1448 
1449  if(!resources::gameboard->map().on_board(loc)) {
1450  return variant();
1451  }
1452 
1453  return variant(un.defense_modifier((resources::gameboard->map())[loc]));
1454  }
1455 
1456  if (u_type)
1457  {
1458  const unit_type& un = u_type->get_unit_type();
1459 
1460  if(!resources::gameboard->map().on_board(loc)) {
1461  return variant();
1462  }
1463 
1464  return variant(un.movement_type().defense_modifier((resources::gameboard->map())[loc]));
1465  }
1466 
1467  return variant();
1468  }
1469 };
1470 
1471 
1472 class movement_cost_function : public function_expression {
1473 public:
1474  movement_cost_function(const args_list& args)
1475  : function_expression("movement_cost", args, 2, 2)
1476  {}
1477 private:
1478  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1479  variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"movement_cost:unit"));
1480  variant loc_var = args()[1]->evaluate(variables,add_debug_info(fdb,0,"movement_cost:location"));
1481  if(u.is_null() || loc_var.is_null()) {
1482  return variant();
1483  }
1484  //we can pass to this function either unit_callable or unit_type callable
1485  const unit_callable* u_call = try_convert_variant<unit_callable>(u);
1486  const unit_type_callable* u_type = try_convert_variant<unit_type_callable>(u);
1487  const map_location& loc = convert_variant<location_callable>(loc_var)->loc();
1488 
1489  if (u_call)
1490  {
1491  const unit& un = u_call->get_unit();
1492 
1493  if(!resources::gameboard->map().on_board(loc)) {
1494  return variant();
1495  }
1496 
1497  return variant(un.movement_cost((resources::gameboard->map())[loc]));
1498  }
1499 
1500  if (u_type)
1501  {
1502  const unit_type& un = u_type->get_unit_type();
1503 
1504  if(!resources::gameboard->map().on_board(loc)) {
1505  return variant();
1506  }
1507 
1508  return variant(un.movement_type().movement_cost((resources::gameboard->map())[loc]));
1509  }
1510 
1511  return variant();
1512  }
1513 };
1514 
1515 class is_avoided_location_function : public function_expression {
1516 public:
1517  is_avoided_location_function(const args_list& args, const formula_ai& ai_object)
1518  : function_expression("is_avoided_location",args, 1, 1), ai_(ai_object)
1519  {}
1520 private:
1521  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1522  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"is_avoided_location:location"));
1523  if(res.is_null()) {
1524  return variant();
1525  }
1526  const map_location& loc = convert_variant<location_callable>(res)->loc();
1527  return variant(ai_.get_avoid().match(loc));
1528  }
1529 
1530  const formula_ai &ai_;
1531 };
1532 
1533 class max_possible_damage_function : public function_expression {
1534 public:
1535  max_possible_damage_function(const args_list& args)
1536  : function_expression("max_possible_damage", args, 2, 2)
1537  {}
1538 private:
1539  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1540  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage:unit1"));
1541  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage:unit2"));
1542  if(u1.is_null() || u2.is_null()) {
1543  return variant();
1544  }
1545  std::vector<attack_type> attacks_tmp;
1546  std::vector<attack_type>& attacks = attacks_tmp;
1547 
1548  //we have to make sure that this function works with any combination of unit_callable/unit_type_callable passed to it
1549  const unit_callable* u_attacker = try_convert_variant<unit_callable>(u1);
1550  if (u_attacker)
1551  {
1552  attacks = u_attacker->get_unit().attacks();
1553  } else
1554  {
1555  const unit_type_callable* u_t_attacker = convert_variant<unit_type_callable>(u1);
1556  attacks_tmp = u_t_attacker->get_unit_type().attacks();
1557  }
1558 
1559  const unit_callable* u_defender = try_convert_variant<unit_callable>(u2);
1560  if (u_defender)
1561  {
1562  const unit& defender = u_defender->get_unit();
1563  int best = 0;
1564  for(std::vector<attack_type>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
1565  const int dmg = round_damage(i->damage(), defender.damage_from(*i, false, map_location()), 100) * i->num_attacks();
1566  if(dmg > best)
1567  best = dmg;
1568  }
1569  return variant(best);
1570  } else
1571  {
1572  const unit_type& defender = convert_variant<unit_type_callable>(u2)->get_unit_type();
1573  int best = 0;
1574  for(std::vector<attack_type>::const_iterator i = attacks.begin(); i != attacks.end(); ++i) {
1575  const int dmg = round_damage(i->damage(), defender.movement_type().resistance_against(*i), 100) * i->num_attacks();
1576  if(dmg > best)
1577  best = dmg;
1578  }
1579  return variant(best);
1580  }
1581  }
1582 };
1583 
1584 
1585 class max_possible_damage_with_retaliation_function : public function_expression {
1586 public:
1587  max_possible_damage_with_retaliation_function(const args_list& args)
1588  : function_expression("max_possible_damage_with_retaliation", args, 2, 2)
1589  {}
1590 private:
1591 
1592  std::pair<int, int> best_melee_and_ranged_attacks(unit_adapter attacker, unit_adapter defender) const {
1593  int highest_melee_damage = 0;
1594  int highest_ranged_damage = 0;
1595 
1596  std::vector<attack_type> attacks = attacker.attacks();
1597 
1598  for (const attack_type &attack : attacks) {
1599  const int dmg = round_damage(attack.damage(), defender.damage_from(attack), 100) * attack.num_attacks();
1600  if (attack.range() == "melee") {
1601  highest_melee_damage = std::max(highest_melee_damage, dmg);
1602  } else {
1603  highest_ranged_damage = std::max(highest_ranged_damage, dmg);
1604  }
1605  }
1606 
1607  return std::make_pair(highest_melee_damage, highest_ranged_damage);
1608  }
1609 
1610  variant execute(const formula_callable& variables, formula_debugger *fdb) const {
1611  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage_with_retaliation:unit1"));
1612  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage_with_retaliation:unit2"));
1613 
1614  if(u1.is_null() || u2.is_null()) {
1615  return variant();
1616  }
1617 
1618  unit_adapter attacker(u1);
1619  unit_adapter defender(u2);
1620 
1621  // find max damage inflicted by attacker and by defender to the attacker
1622  std::pair<int, int> best_attacker_attacks = best_melee_and_ranged_attacks(attacker, defender);
1623  std::pair<int, int> best_defender_attacks = best_melee_and_ranged_attacks(defender, attacker);
1624 
1625  std::vector<variant> vars;
1626  vars.push_back(variant(best_attacker_attacks.first));
1627  vars.push_back(variant(best_attacker_attacks.second));
1628  vars.push_back(variant(best_defender_attacks.first));
1629  vars.push_back(variant(best_defender_attacks.second));
1630 
1631  return variant(&vars);
1632  }
1633 };
1634 
1635 template<typename T>
1636 class ai_formula_function : public formula_function {
1637 protected:
1638  formula_ai& ai_;
1639 public:
1640  ai_formula_function(const std::string& name, ai::formula_ai& ai) : formula_function(name), ai_(ai) {}
1641  function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const {
1642  return function_expression_ptr(new T(args, ai_));
1643  }
1644 };
1645 
1646 }
1647 
1648 // First macro is for functions taking an additional formula_ai argument.
1649 // Functions using the second macro could potentially be made core.
1650 #define AI_FUNCTION(name) add_function(#name, formula_function_ptr( \
1651  new ai_formula_function<name##_function>(#name, ai)))
1652 #define FUNCTION(name) add_function(#name, formula_function_ptr( \
1653  new builtin_formula_function<name##_function>(#name)))
1654 
1656  FUNCTION(outcomes);
1657  //AI_FUNCTION(evaluate_for_position);
1658  FUNCTION(move);
1659  FUNCTION(move_partial);
1660  FUNCTION(attack);
1661  AI_FUNCTION(rate_action);
1662  FUNCTION(recall);
1663  FUNCTION(recruit);
1664  FUNCTION(safe_call);
1666  AI_FUNCTION(is_avoided_location);
1667  FUNCTION(is_village);
1668  AI_FUNCTION(is_unowned_village);
1669  FUNCTION(unit_at);
1671  FUNCTION(set_var);
1672  FUNCTION(set_unit_var);
1673  FUNCTION(fallback);
1674  FUNCTION(units_can_reach);
1675  AI_FUNCTION(debug_label);
1676  FUNCTION(defense_on);
1677  FUNCTION(chance_to_hit);
1678  FUNCTION(movement_cost);
1679  FUNCTION(max_possible_damage);
1680  FUNCTION(max_possible_damage_with_retaliation);
1681  AI_FUNCTION(next_hop);
1682  FUNCTION(adjacent_locs);
1683  FUNCTION(locations_in_radius);
1684  FUNCTION(castle_locs);
1685  FUNCTION(timeofday_modifier);
1686  AI_FUNCTION(distance_to_nearest_unowned_village);
1687  AI_FUNCTION(shortest_path);
1688  AI_FUNCTION(simplest_path);
1689  AI_FUNCTION(nearest_keep);
1690  AI_FUNCTION(suitable_keep);
1691  FUNCTION(nearest_loc);
1692  AI_FUNCTION(find_shroud);
1693  AI_FUNCTION(close_enemies);
1694  FUNCTION(calculate_outcome);
1695  AI_FUNCTION(run_file);
1696  AI_FUNCTION(calculate_map_ownership);
1697 }
1698 #undef FUNCTION
1699 
1700 }
Defines formula ai.
size_t num_elements() const
Definition: variant.cpp:526
int total_movement() const
Definition: unit.hpp:218
#define ERR_AI
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator *calc, const size_t width, const size_t height, const teleport_map *teleports, bool border)
std::vector< double > hp_dist
Resulting probability distribution (might be not as large as max_hp)
unit_iterator end()
Definition: map.hpp:311
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_
boost::shared_ptr< formula > formula_ptr
Definition: formula_fwd.hpp:24
Definition: unit.hpp:95
#define LOG_AI
int movement() const
Definition: types.hpp:128
GLenum GLint * range
Definition: glew.h:3025
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
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:410
SDL_Color int_to_color(const Uint32 rgb)
Definition: utils.cpp:63
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:85
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
bool is_village(const map_location &loc) const
Definition: map.cpp:68
std::map< map_location, pathfind::paths > moves_map
The standard way in which a map of possible movement routes to location is recorded.
Definition: game_info.hpp:50
void get_tiles_in_radius(const map_location &center, const int radius, std::vector< map_location > &result)
Function that will add to result all locations within radius tiles of center (excluding center itself...
Definition: pathutils.cpp:56
const movetype & movement_type() const
Definition: types.hpp:150
General purpose widgets.
unit_iterator begin()
Definition: map.hpp:308
const map_location & loc() const
unit_type_data unit_types
Definition: types.cpp:1314
Replay control code.
GLuint GLdouble u1
Definition: glew.h:2972
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
GLenum src
Definition: glew.h:2392
const std::vector< std::string > items
double chance_to_kill
Estimated % chance to kill the unit.
Definition: contexts.hpp:107
const unit * unit_
GLdouble GLdouble t
Definition: glew.h:1366
int movement_cost(const t_translation::t_terrain &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition: movetype.hpp:199
#define WRN_AI
bool is_null() const
Definition: variant.hpp:80
int defense_modifier(const t_translation::t_terrain &terrain) const
Definition: unit.cpp:1520
const unit & get_unit() const
GLdouble l
Definition: glew.h:6966
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const SDL_Color 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:145
const formula_ai & ai_
map_location loc_
GLdouble GLdouble GLdouble b
Definition: glew.h:6966
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:47
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:57
GLsizei const char ** path
Definition: glew.h:4654
GLuint GLuint end
Definition: glew.h:1221
std::vector< map_location > steps
Definition: pathfind.hpp:135
unit_type::ALIGNMENT alignment() const
Definition: unit.hpp:368
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:132
bool is_list() const
Definition: variant.hpp:92
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
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
int as_int() const
Definition: variant.cpp:558
const GLdouble * v
Definition: glew.h:1359
GLenum GLenum dst
Definition: glew.h:2392
#define AI_FUNCTION(name)
int w() const
Effective map width.
Definition: map.hpp:105
const unit_type & get_unit_type() const
game_board * gameboard
Definition: resources.cpp:20
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
Encapsulates the map of the game.
Definition: map.hpp:37
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:135
int h
std::string string_cast() const
Definition: variant.cpp:1165
map_display and display: classes which take care of displaying the map and game-data on the screen...
replay * recorder
Definition: resources.cpp:30
Function which only uses terrain, ignoring shroud, enemies, etc.
Definition: pathfind.hpp:241
int damage_from(const attack_type &attack, bool attacker, const map_location &loc) const
Definition: unit.hpp:274
static const map_location & null_location()
Definition: location.hpp:195
static const ::config * terrain
The terrain used to create the cache.
Definition: minimap.cpp:135
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
GLsizei const GLint * locations
Definition: glew.h:11075
GLuint GLuint GLsizei count
Definition: glew.h:1221
GLuint color
Definition: glew.h:5801
const std::vector< attack_type > & attacks() const
Definition: unit.hpp:271
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
boost::shared_ptr< function_expression > function_expression_ptr
Definition: function.hpp:98
Encapsulates the map of the game.
Definition: location.hpp:38
const terrain_type & get_terrain_info(const t_translation::t_terrain &terrain) const
Definition: map.cpp:100
GLuint res
Definition: glew.h:9258
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: util.hpp:60
boost::shared_ptr< formula_expression > expression_ptr
Definition: formula.hpp:26
static config unit_moves(reports::context &rc, const unit *u)
Definition: reports.cpp:582
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:213
int h() const
Effective map height.
Definition: map.hpp:108
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...
const location & get_location() const
const std::string & as_string() const
Definition: variant.cpp:603
GLuint index
Definition: glew.h:1782
static bool operator<(const placing_info &a, const placing_info &b)
Definition: game_state.cpp:127
Default AI contexts.
GLfloat GLfloat GLfloat GLfloat h
Definition: glew.h:5910
size_t i
Definition: function.cpp:1057
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
Declarations for File-IO.
int w
GLdouble GLdouble GLdouble r
Definition: glew.h:1374
int main(int argc, char **argv)
static lg::log_domain log_formula_ai("ai/engine/fai")
GLuint const GLchar * name
Definition: glew.h:1782
virtual const gamemap & map() const
Definition: game_board.hpp:98
#define next(ls)
Definition: llex.cpp:27
GLsizeiptr size
Definition: glew.h:1649
std::string to_debug_string(std::vector< const game_logic::formula_callable * > *seen=nullptr, bool verbose=false) const
Definition: variant.cpp:1229
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition: unit.cpp:196
To store label data Class implements logic for rendering.
Definition: label.hpp:103
GLclampd n
Definition: glew.h:5903
const GLdouble * m
Definition: glew.h:6968
bool find(E event, F functor)
Tests whether an event handler is available.
std::vector< attack_type > attacks() const
Definition: types.cpp:484
const unit_type * unit_type_
bool is_fearless() const
Definition: unit.hpp:306
Standard logging facilities (interface).
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:71
Container associating units to locations.
Definition: map.hpp:90
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1155
map_labels & labels()
Definition: display.cpp:2773
unit_iterator find(size_t id)
Definition: map.cpp:285
static Uint32 get_side_rgb(int side)
Definition: team.hpp:367
#define FUNCTION(name)
bool is_string() const
Definition: variant.hpp:79
const std::string weapon
GLuint GLdouble GLdouble u2
Definition: glew.h:2972
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
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:163
This module contains various pathfinding functions and utilities.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
int defense_modifier(const t_translation::t_terrain &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:209
double rating(double aggression, const readonly_context &ai_obj) const
Definition: attack.cpp:264
void add_label(const terrain_label *)
Definition: replay.cpp:266
const std::string valid
Little parts of regex templates used to parse Wml annoations.
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:1649