The Battle for Wesnoth  1.19.0-dev
function_table.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
3  by Bartosz Waresiak <dragonking@o2.pl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #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 "global.hpp"
32 #include "display.hpp"
33 #include "log.hpp"
34 #include "map/label.hpp"
35 #include "map/map.hpp"
36 #include "pathfind/teleport.hpp"
37 #include "replay.hpp"
38 #include "resources.hpp"
39 #include "color.hpp"
40 #include "terrain/filter.hpp"
41 #include "units/unit.hpp"
42 #include "units/types.hpp"
43 #include "pathfind/pathfind.hpp"
44 
45 static lg::log_domain log_formula_ai("ai/engine/fai");
46 #define LOG_AI LOG_STREAM(info, log_formula_ai)
47 #define WRN_AI LOG_STREAM(warn, log_formula_ai)
48 #define ERR_AI LOG_STREAM(err, log_formula_ai)
49 
50 namespace wfl {
51 using ai::formula_ai;
52 
53 namespace {
54 
55 /*
56  * unit adapters let us treat unit and unit_type the same if we want to get access to attacks or movement cost
57  */
58 class unit_adapter {
59  public:
60  unit_adapter(const variant& arg) : unit_type_(), unit_() {
61  auto unit = arg.try_convert<unit_callable>();
62 
63  if (unit) {
64  unit_ = &unit->get_unit();
65  } else {
66  unit_type_ = &(arg.convert_to<unit_type_callable>()->get_unit_type());
67  }
68  }
69 
70  int damage_from(const attack_type& attack) const {
71  if(unit_type_ != nullptr) {
73  } else {
74  return unit_->damage_from(attack, false, map_location());
75  }
76  }
77 
78  const_attack_itors attacks() const {
79  if(unit_type_ != nullptr) {
80  return unit_type_->attacks();
81  } else {
82  return unit_->attacks();
83  }
84  }
85 
86  int movement_cost(const t_translation::terrain_code & terrain) const {
87  if(unit_type_ != nullptr) {
88  return unit_type_->movement_type().movement_cost(terrain);
89  } else {
90  return unit_->movement_cost(terrain);
91  }
92  }
93 
94  private:
96  const unit* unit_;
97 };
98 
99 #define DEFINE_FAI_FUNCTION(name, min_args, max_args) \
100  class name##_function : public function_expression \
101  { \
102  public: \
103  explicit name##_function(const args_list& args, const formula_ai& ai) \
104  : function_expression(#name, args, min_args, max_args), ai_(ai) \
105  { \
106  } \
107  \
108  private: \
109  const formula_ai& ai_; \
110  variant execute(const formula_callable& variables, formula_debugger* fdb) const; \
111  }; \
112  \
113  variant name##_function::execute(const formula_callable& variables, formula_debugger* fdb) const
114 
115 DEFINE_FAI_FUNCTION(distance_to_nearest_unowned_village, 1, 1)
116 {
117  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "distance_to_nearest_unowned_village:location")).convert_to<location_callable>()->loc();
118  int best = 1000000;
119  const std::vector<map_location>& villages = resources::gameboard->map().villages();
120  const std::set<map_location>& my_villages = ai_.current_team().villages();
121  for(std::vector<map_location>::const_iterator i = villages.begin(); i != villages.end(); ++i) {
122  int distance = distance_between(loc, *i);
123  if(distance < best) {
124  if(my_villages.count(*i) == 0) {
125  best = distance;
126  }
127  }
128  }
129 
130  return variant(best);
131 }
132 
133 static unsigned search_counter;
134 
135 namespace {
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 formula_ai& ai_)
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.data());
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  }
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 
253 DEFINE_FAI_FUNCTION(calculate_map_ownership, 2, 5)
254 {
255  int w = resources::gameboard->map().w();
256  int h = resources::gameboard->map().h();
257 
258  const variant units_input = args()[0]->evaluate(variables,fdb);
259  const variant leaders_input = args()[1]->evaluate(variables,fdb);
260 
261  int enemy_tolerance = 3;
262  if( args().size() > 2 )
263  enemy_tolerance = args()[2]->evaluate(variables,fdb).as_int();
264 
265  int enemy_border_tolerance = 5;
266  if( args().size() > 3 )
267  enemy_border_tolerance = args()[3]->evaluate(variables,fdb).as_int();
268 
269  int ally_tolerance = 3;
270  if( args().size() > 4 )
271  ally_tolerance = args()[4]->evaluate(variables,fdb).as_int();
272 
273  if( !units_input.is_list() )
274  return variant();
275 
276  std::size_t number_of_teams = units_input.num_elements();
277 
278  std::vector< std::vector<int>> scores( number_of_teams );
279 
280  for( std::size_t i = 0; i< number_of_teams; ++i)
281  scores[i].resize(w*h);
282 
283  /* // TODO: Do we need this?
284  for(unit_map::const_iterator i = resources::gameboard->units().begin(); i != resources::gameboard->units().end(); ++i) {
285  unit_counter[i->second.side()-1]++;
286  unit_adapter unit(i->second);
287  find_movemap( resources::gameboard->map(), resources::gameboard->units(), unit, i->first, scores[i->second.side()-1], ai_.resources::gameboard->teams() , true );
288  }
289  */
290 
291  for(std::size_t side = 0 ; side < units_input.num_elements() ; ++side) {
292  if( leaders_input[side].is_empty() )
293  continue;
294 
295  const map_location loc = leaders_input[side][0].convert_to<location_callable>()->loc();
296  const variant units_of_side = units_input[side];
297 
298  for(std::size_t unit_it = 0 ; unit_it < units_of_side.num_elements() ; ++unit_it) {
299  unit_adapter unit(units_of_side[unit_it]);
300  find_movemap( unit, loc, scores[side], true, ai_ );
301  }
302  }
303 
304  std::size_t index = 0;
305  for( std::vector< std::vector<int>>::iterator i = scores.begin() ; i != scores.end() ; ++i) {
306  for( std::vector<int>::iterator j = i->begin() ; j != i->end() ; ++j ) {
307  if(units_input[index].num_elements() != 0) {
308  *j /= units_input[index].num_elements();
309  } else {
310  *j = 0;
311  }
312  }
313 
314  ++index;
315  }
316  //std::vector<variant> res;
317  std::map<variant, variant> res;
318 
319  std::size_t current_side = ai_.get_side() - 1 ;
320 
321  std::vector<int> enemies;
322  std::vector<int> allies;
323 
324  for(std::size_t side = 0 ; side < units_input.num_elements() ; ++side) {
325  if( side == current_side)
326  continue;
327 
328  if( ai_.current_team().is_enemy(side+1) ) {
329  if( !leaders_input[side].is_empty() )
330  enemies.push_back(side);
331  } else {
332  if( !leaders_input[side].is_empty() )
333  allies.push_back(side);
334  }
335  }
336 
337  //calculate_map_ownership( recruits_of_side, map(units_of_side, 'units', map( filter(units, leader), loc) ) )
338  //map(, debug_label(key,value))
339  for (int x = 0; x < w; ++x) {
340  for (int y = 0; y < h; ++y)
341  {
342  int i = y * w + x;
343  bool valid = true;
344  bool enemy_border = false;
345 
346  if( scores[current_side][i] > 98 )
347  continue;
348 
349  for (int side : enemies) {
350  int diff = scores[current_side][i] - scores[side][i];
351  if ( diff > enemy_tolerance) {
352  valid = false;
353  break;
354  } else if( std::abs(diff) < enemy_border_tolerance )
355  enemy_border = true;
356  }
357 
358  if( valid ) {
359  for (int side : allies) {
360  if ( scores[current_side][i] - scores[side][i] > ally_tolerance ) {
361  valid = false;
362  break;
363  }
364  }
365  }
366 
367  if( valid ) {
368  if( enemy_border )
369  res.emplace(variant(std::make_shared<location_callable>(map_location(x, y))), variant(scores[0][i] + 10000));
370  else
371  res.emplace(variant(std::make_shared<location_callable>(map_location(x, y))), variant(scores[0][i]));
372  }
373  }
374  }
375  return variant(res);
376 }
377 
378 DEFINE_WFL_FUNCTION(nearest_loc, 2, 2)
379 {
380  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_loc:location")).convert_to<location_callable>()->loc();
381  variant items = args()[1]->evaluate(variables,add_debug_info(fdb,1,"nearest_loc:locations"));
382  int best = 1000000;
383  int best_i = -1;
384 
385  for(std::size_t i = 0; i < items.num_elements(); ++i) {
386 
387  const map_location move_loc = items[i].convert_to<location_callable>()->loc();
388  int distance = distance_between(loc, move_loc);
389 
390  if(distance < best) {
391  best = distance;
392  best_i = i;
393  }
394  }
395 
396  if( best_i != -1)
397  return variant(std::make_shared<location_callable>(items[best_i].convert_to<location_callable>()->loc()));
398  else
399  return variant();
400 }
401 
402 /** FormulaAI function to run fai script from file. Usable from in-game console.
403 * arguments[0] - required file name, follows the usual wml convention
404 */
405 DEFINE_FAI_FUNCTION(run_file, 1, 1)
406 {
407  const args_list& arguments = args();
408  const variant var0 = arguments[0]->evaluate(variables,add_debug_info(fdb,0,"run_file:file"));
409  const std::string filename = var0.string_cast();
410 
411  //NOTE: get_wml_location also filters file path to ensure it doesn't contain things like "../../top/secret"
412  std::string path = filesystem::get_wml_location(filename);
413  if(path.empty()) {
414  ERR_AI << "run_file : not found [" << filename <<"]";
415  return variant(); //no suitable file
416  }
417 
418  std::string formula_string = filesystem::read_file(path);
419  //need to get function_table from somewhere or delegate to someone who has access to it
420  formula_ptr parsed_formula = ai_.create_optional_formula(formula_string);
421  if(parsed_formula == formula_ptr()) {
422  ERR_AI << "run_file : unable to create formula";
423  return variant(); //was unable to create a formula from file
424  }
425  return parsed_formula->evaluate(variables,add_debug_info(fdb,-1,"run_file:formula_from_file"));
426 }
427 
428 DEFINE_WFL_FUNCTION(castle_locs, 1, 1)
429 {
430  const map_location starting_loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "castle_locs:location")).convert_to<location_callable>()->loc();
431 
432  //looks like reimplementing a generic graph search algorithm to me
433  std::set< map_location > visited_locs;
434  std::queue< map_location > queued_locs;
435 
436  queued_locs.push(starting_loc);
437 
438  while( !queued_locs.empty() ) {
439  const map_location loc = queued_locs.front();
440  queued_locs.pop();
441 
442  if ( visited_locs.find( loc ) != visited_locs.end() )
443  continue;
444 
445  visited_locs.insert(loc);
446 
447  for(const map_location& adj : get_adjacent_tiles(loc)) {
448  if (resources::gameboard->map().on_board(adj) && visited_locs.find( adj ) == visited_locs.end() ) {
449  if (resources::gameboard->map().get_terrain_info(adj).is_keep() ||
450  resources::gameboard->map().get_terrain_info(adj).is_castle() ) {
451  queued_locs.push(adj);
452  }
453  }
454  }
455  }
456 
457  if ( !resources::gameboard->map().get_terrain_info(starting_loc).is_keep() &&
458  !resources::gameboard->map().get_terrain_info(starting_loc).is_castle() )
459  visited_locs.erase(starting_loc);
460 
461  std::vector<variant> res;
462  for (const map_location& ml : visited_locs) {
463  res.emplace_back(std::make_shared<location_callable>( ml ));
464  }
465 
466  return variant(res);
467 }
468 
469 /**
470  * timeofday_modifer formula function. Returns combat modifier, taking
471  * alignment, illuminate, time of day and fearless trait into account.
472  * 'leadership' and 'slowed' are not taken into account.
473  * arguments[0] - unit
474  * arguments[1] - location (optional, defaults to unit's current location.
475  */
476 DEFINE_WFL_FUNCTION(timeofday_modifier, 1, 2)
477 {
478  variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"timeofday_modifier:unit"));
479 
480  if( u.is_null() ) {
481  return variant();
482  }
483 
484  auto u_call = u.try_convert<unit_callable>();
485 
486  if(!u_call) {
487  return variant();
488  }
489 
490  const unit& un = u_call->get_unit();
491 
492  map_location loc;
493 
494  if(args().size()==2) {
495  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "timeofday_modifier:location")).convert_to<location_callable>()->loc();
496  }
497 
498  if(!loc.valid()) {
499  loc = u_call->get_location();
500  }
501 
502  return variant(combat_modifier(resources::gameboard->units(), resources::gameboard->map(), loc, un.alignment(), un.is_fearless()));
503 }
504 
505 DEFINE_FAI_FUNCTION(nearest_keep, 1, 1)
506 {
507  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "nearest_keep:location")).convert_to<location_callable>()->loc();
508  int best = 1000000;
509  int best_i = -1;
510 
511  ai_.get_keeps();
513 
514  for( int i = 0 ; i < size; ++i) {
515  int distance = distance_between(loc, ai_.get_keeps_cache()[i].convert_to<location_callable>()->loc() );
516  if(distance < best)
517  {
518  best = distance;
519  best_i = i;
520  }
521  }
522 
523  if( best_i != -1)
524  return variant(std::make_shared<location_callable>(ai_.get_keeps_cache()[best_i].convert_to<location_callable>()->loc()));
525  else
526  return variant();
527 }
528 
529 /**
530 * Find suitable keep for unit at location
531 * arguments[0] - location for unit on which the suitable keep is to be found
532 */
533 DEFINE_FAI_FUNCTION(suitable_keep, 1, 1)
534 {
535  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "suitable_keep:location")).convert_to<location_callable>()->loc();
536  const unit_map& units = resources::gameboard->units();
537  const unit_map::const_iterator u = units.find(loc);
538  if (u == units.end()){
539  return variant();
540  }
541  const pathfind::paths unit_paths(*u, false, true, ai_.current_team());
542  return variant(std::make_shared<location_callable>(ai_.suitable_keep(loc,unit_paths)));
543 }
544 
545 DEFINE_FAI_FUNCTION(find_shroud, 0, 1)
546 {
547  std::vector<variant> vars;
548  int w,h;
549 
550  if(args().size()==1) {
551  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "find_shroud:gamemap")).convert_to<gamemap_callable>()->get_gamemap();
552  w = m.w();
553  h = m.h();
554  } else {
555  w = resources::gameboard->map().w();
556  h = resources::gameboard->map().h();
557  }
558 
559  for(int i = 0; i < w; ++i)
560  for(int j = 0; j < h; ++j) {
562  vars.emplace_back(std::make_shared<location_callable>(map_location(i, j)));
563  }
564 
565  return variant(vars);
566 }
567 
568 DEFINE_FAI_FUNCTION(close_enemies, 2, 2)
569 {
570  std::vector<variant> vars;
571  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "close_enemies:location")).convert_to<location_callable>()->loc();
572  int range_s = args()[1]->evaluate(variables,add_debug_info(fdb,1,"close_enemies:distance")).as_int();
573  if (range_s < 0) {
574  WRN_AI << "close_enemies_function: range is negative (" << range_s << ")";
575  range_s = 0;
576  }
577  std::size_t range = static_cast<std::size_t>(range_s);
580  while (un != end) {
581  if (distance_between(loc, un->get_location()) <= range) {
582  if (un->side() != ai_.get_side()) {//fixme: ignores allied units
583  vars.emplace_back(std::make_shared<unit_callable>(*un));
584  }
585  }
586  ++un;
587  }
588  return variant(vars);
589 }
590 
591 DEFINE_WFL_FUNCTION(calculate_outcome, 3, 4)
592 {
593  std::vector<variant> vars;
594  int weapon;
595  if (args().size() > 3) weapon = args()[3]->evaluate(variables,add_debug_info(fdb,3,"calculate_outcome:weapon")).as_int();
596  else weapon = -1;
597 
598  const unit_map& units = resources::gameboard->units();
599  map_location attacker_location =
600  args()[0]->evaluate(variables, add_debug_info(fdb, 0, "calculate_outcome:attacker_current_location")).convert_to<location_callable>()->loc();
601  if(units.count(attacker_location) == 0) {
602  ERR_AI << "Performing calculate_outcome() with non-existent attacker at (" <<
603  attacker_location.wml_x() << "," << attacker_location.wml_y() << ")";
604  return variant();
605  }
606 
607  map_location defender_location =
608  args()[2]->evaluate(variables,add_debug_info(fdb, 2, "calculate_outcome:defender_location")).convert_to<location_callable>()->loc();
609  if(units.count(defender_location) == 0) {
610  ERR_AI << "Performing calculate_outcome() with non-existent defender at (" <<
611  defender_location.wml_x() << "," << defender_location.wml_y() << ")";
612  return variant();
613  }
614 
615  battle_context bc(units, args()[1]->evaluate(variables, add_debug_info(fdb, 1, "calculate_outcome:attacker_attack_location")).convert_to<location_callable>()->loc(),
616  defender_location, weapon, -1, 1.0, nullptr, units.find(attacker_location).get_shared_ptr());
617  std::vector<double> hp_dist = bc.get_attacker_combatant().hp_dist;
618  std::vector<double>::iterator it = hp_dist.begin();
619  int i = 0;
620  std::vector<variant> hitLeft;
621  std::vector<variant> prob;
622  while (it != hp_dist.end()) {
623  if (*it != 0) {
624  hitLeft.emplace_back(i);
625  prob.emplace_back(static_cast<int>(*it*10000));
626  }
627  ++it;
628  ++i;
629  }
630  std::vector<variant> status;
631  if (bc.get_attacker_combatant().poisoned != 0)
632  status.emplace_back("Poisoned");
633  if (bc.get_attacker_combatant().slowed != 0)
634  status.emplace_back("Slowed");
635  if (bc.get_defender_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_attacker_stats().hp)
636  status.emplace_back("Stoned");
637  if (bc.get_defender_stats().plagues && hitLeft[0].as_int() == 0)
638  status.emplace_back("Zombiefied");
639  vars.emplace_back(std::make_shared<outcome_callable>(hitLeft, prob, status));
640  hitLeft.clear();
641  prob.clear();
642  status.clear();
643  hp_dist = bc.get_defender_combatant().hp_dist;
644  it = hp_dist.begin();
645  i = 0;
646  while (it != hp_dist.end()) {
647  if (*it != 0) {
648  hitLeft.emplace_back(i);
649  prob.emplace_back(static_cast<int>(*it*10000));
650  }
651  ++it;
652  ++i;
653  }
654  if (bc.get_defender_combatant().poisoned != 0)
655  status.emplace_back("Poisoned");
656  if (bc.get_defender_combatant().slowed != 0)
657  status.emplace_back("Slowed");
658  if (bc.get_attacker_stats().petrifies && static_cast<unsigned int>(hitLeft[0].as_int()) != bc.get_defender_stats().hp)
659  status.emplace_back("Stoned");
660  if (bc.get_attacker_stats().plagues && hitLeft[0].as_int() == 0)
661  status.emplace_back("Zombiefied");
662  vars.emplace_back(std::make_shared<outcome_callable>(hitLeft, prob, status));
663  return variant(vars);
664 }
665 
666 DEFINE_WFL_FUNCTION(outcomes, 1, 1)
667 {
668  variant attack = args()[0]->evaluate(variables,add_debug_info(fdb,0,"outcomes:attack"));
669  auto analysis = attack.convert_to<ai::attack_analysis>();
670  //unit_map units_with_moves(resources::gameboard->units());
671  //typedef std::pair<map_location, map_location> mv;
672  //for(const mv &m : analysis->movements) {
673  // units_with_moves.move(m.first, m.second);
674  //}
675 
676  std::vector<variant> vars;
677  if(analysis->chance_to_kill > 0.0) {
678  //unit_map units(units_with_moves);
679  //units.erase(analysis->target);
680  vars.emplace_back(std::make_shared<position_callable>(/*&units,*/ static_cast<int>(analysis->chance_to_kill*100)));
681 
682  }
683 
684  if(analysis->chance_to_kill < 1.0) {
685  //unit_map units(units_with_moves);
686  vars.emplace_back(std::make_shared<position_callable>(/*&units,*/ static_cast<int>(100 - analysis->chance_to_kill*100)));
687  }
688 
689  return variant(vars);
690 }
691 
692 DEFINE_FAI_FUNCTION(rate_action, 1, 1)
693 {
694  variant act = args()[0]->evaluate(variables,add_debug_info(fdb,0,"rate_action:action"));
695  auto analysis = act.convert_to<ai::attack_analysis>();
696 
697  return variant(analysis->rating(ai_.get_aggression(),ai_)*1000,variant::DECIMAL_VARIANT);
698 }
699 
700 DEFINE_WFL_FUNCTION(recall, 1, 2)
701 {
702  const std::string id = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recall:id")).as_string();
703  map_location loc;
704  if(args().size() >= 2) {
705  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "recall:location")).convert_to<location_callable>()->loc();
706  }
707 
708  return variant(std::make_shared<recall_callable>(loc, id));
709 }
710 
711 DEFINE_WFL_FUNCTION(recruit, 1, 2)
712 {
713  const std::string type = args()[0]->evaluate(variables,add_debug_info(fdb,0,"recruit:type")).as_string();
714  map_location loc;
715  if(args().size() >= 2) {
716  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "recruit:location")).convert_to<location_callable>()->loc();
717  }
718 
719  return variant(std::make_shared<recruit_callable>(loc, type));
720 }
721 
722 DEFINE_FAI_FUNCTION(shortest_path, 2, 3)
723 {
724 
725  std::vector<variant> locations;
726 
727  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "shortest_path:src")).convert_to<location_callable>()->loc();
728  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "shortest_path:dst")).convert_to<location_callable>()->loc();
729  map_location unit_loc;
730 
731  if( src == dst )
732  return variant(locations);
733 
734  if(args().size() > 2)
735  unit_loc = args()[2]->evaluate(variables,add_debug_info(fdb,2,"shortest_path:unit_location")).convert_to<location_callable>()->loc();
736  else
737  unit_loc = src;
738 
739  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
740 
741  if( unit_it == resources::gameboard->units().end() ) {
742  std::ostringstream str;
743  str << "shortest_path function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
744  throw formula_error( str.str(), "", "", 0);
745  }
746 
747  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
748 
749  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
750 
751  if( route.steps.size() < 2 ) {
752  return variant(locations);
753  }
754 
755  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
756  locations.emplace_back(std::make_shared<location_callable>(*loc_iter));
757  }
758 
759  return variant(locations);
760 }
761 
762 DEFINE_FAI_FUNCTION(simplest_path, 2, 3)
763 {
764  std::vector<variant> locations;
765 
766  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "simplest_path:src")).convert_to<location_callable>()->loc();
767  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "simplest_path:dst")).convert_to<location_callable>()->loc();
768  map_location unit_loc;
769 
770  if( src == dst )
771  return variant(locations);
772 
773  if(args().size() > 2)
774  unit_loc = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "simplest_path:unit_location")).convert_to<location_callable>()->loc();
775  else
776  unit_loc = src;
777 
778  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
779 
780  if( unit_it == resources::gameboard->units().end() ) {
781  std::ostringstream str;
782  str << "simplest_path function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
783  throw formula_error( str.str(), "", "", 0);
784  }
785 
786  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
787 
789 
790  pathfind::plain_route route = pathfind::a_star_search(src, dst, 1000.0, em_calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
791 
792  if( route.steps.size() < 2 ) {
793  return variant(locations);
794  }
795 
796  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
797  if(unit_it->movement_cost(static_cast<const game_board*>(resources::gameboard)->map()[*loc_iter]) < movetype::UNREACHABLE) {
798  locations.emplace_back(std::make_shared<location_callable>(*loc_iter));
799  } else {
800  break;
801  }
802  }
803 
804  return variant(locations);
805 }
806 
807 DEFINE_FAI_FUNCTION(next_hop, 2, 3)
808 {
809 
810  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "next_hop:src")).convert_to<location_callable>()->loc();
811  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "next_hop:dst")).convert_to<location_callable>()->loc();
812  map_location unit_loc;
813 
814  if( src == dst )
815  return variant();
816 
817  if(args().size() > 2)
818  unit_loc = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "next_hop:unit_location")).convert_to<location_callable>()->loc();
819  else
820  unit_loc = src;
821 
822  unit_map::iterator unit_it = resources::gameboard->units().find(unit_loc);
823 
824  if( unit_it == resources::gameboard->units().end() ) {
825  std::ostringstream str;
826  str << "next_hop function: expected unit at location (" << (unit_loc.wml_x()) << "," << (unit_loc.wml_y()) << ")";
827  throw formula_error( str.str(), "", "", 0);
828  }
829 
830  pathfind::teleport_map allowed_teleports = ai_.get_allowed_teleports(unit_it);
831 
832  pathfind::plain_route route = ai_.shortest_path_calculator( src, dst, unit_it, allowed_teleports );
833 
834  if( route.steps.size() < 2 ) {
835  return variant();
836  }
837 
839  const ai::moves_map &possible_moves = ai_.get_possible_moves();
840  const ai::moves_map::const_iterator& p_it = possible_moves.find(unit_loc);
841  if (p_it==possible_moves.end() ) {
842  return variant();
843  }
844 
845  for (std::vector<map_location>::const_iterator loc_iter = route.steps.begin() + 1 ; loc_iter !=route.steps.end(); ++loc_iter) {
846 
847  if (p_it->second.destinations.find(*loc_iter) != p_it->second.destinations.end() ) {
848  loc = *loc_iter;
849  } else {
850  break;
851  }
852  }
853  if (loc==map_location::null_location()) {
854  return variant();
855  }
856  return variant(std::make_shared<location_callable>(loc));
857 }
858 
859 DEFINE_WFL_FUNCTION(move, 2, 2)
860 {
861  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "move:src")).convert_to<location_callable>()->loc();
862  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "move:dst")).convert_to<location_callable>()->loc();
863  LOG_AI << "move(): " << src << ", " << dst << ")";
864  return variant(std::make_shared<move_callable>(src, dst));
865 }
866 
867 DEFINE_WFL_FUNCTION(move_partial, 2, 2)
868 {
869  const map_location src = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "move_partial:src")).convert_to<location_callable>()->loc();
870  const map_location dst = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "move_partial:dst")).convert_to<location_callable>()->loc();
871  LOG_AI << "move_partial(): " << src << ", " << dst << ")";
872  return variant(std::make_shared<move_partial_callable>(src, dst));
873 }
874 
875 DEFINE_WFL_FUNCTION(set_unit_var, 3, 3)
876 {
877  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()));
878 }
879 
880 DEFINE_WFL_FUNCTION(fallback, 0, 1)
881 {
882  UNUSED(fdb);
883  // The parameter is not used, but is accepted for legacy compatibility
884  if(args().size() == 1 && args()[0]->evaluate(variables).as_string() != "human")
885  return variant();
886  return variant(std::make_shared<fallback_callable>());
887 }
888 
889 DEFINE_WFL_FUNCTION(attack, 3, 4)
890 {
891  const map_location move_from = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "attack:move_from")).convert_to<location_callable>()->loc();
892  const map_location src = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "attack:src")).convert_to<location_callable>()->loc();
893  const map_location dst = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "attack:dst")).convert_to<location_callable>()->loc();
894  const int weapon = args().size() == 4 ? args()[3]->evaluate(variables,add_debug_info(fdb,3,"attack:weapon")).as_int() : -1;
895  if(resources::gameboard->units().count(move_from) == 0 || resources::gameboard->units().count(dst) == 0) {
896  ERR_AI << "AI ERROR: Formula produced illegal attack: " << move_from << " -> " << src << " -> " << dst;
897  return variant();
898  }
899  return variant(std::make_shared<attack_callable>(move_from, src, dst, weapon));
900 }
901 
902 DEFINE_FAI_FUNCTION(debug_label, 2, 2)
903 {
904  const args_list& arguments = args();
905  const variant var0 = arguments[0]->evaluate(variables,fdb);
906  const variant var1 = arguments[1]->evaluate(variables,fdb);
907 
908  const map_location location = var0.convert_to<location_callable>()->loc();
909  std::string text;
910  if( var1.is_string() )
911  text = var1.as_string();
912  else
913  text = var1.to_debug_string();
914 
916  std::string team_name;
917 
919 
920  const terrain_label *res;
921  res = gui->labels().set_label(location, text, ai_.get_side() - 1, team_name, color);
922  if (res && resources::recorder)
924 
925  std::vector<variant> result;
926  result.push_back(var0);
927  result.push_back(var1);
928  return variant(result);
929 }
930 
931 DEFINE_WFL_FUNCTION(is_village, 2, 3)
932 {
933  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "is_village:map")).convert_to<gamemap_callable>()->get_gamemap();
934 
935  map_location loc;
936  if(args().size() == 2) {
937  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "is_village:location")).convert_to<location_callable>()->loc();
938  } else {
939  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_village:x")).as_int(),
940  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_village:y")).as_int(), wml_loc());
941  }
942  return variant(m.is_village(loc));
943 }
944 
945 DEFINE_FAI_FUNCTION(is_unowned_village, 2, 3)
946 {
947 
948  const gamemap& m = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "is_unowned_village:map")).convert_to<gamemap_callable>()->get_gamemap();
949  const std::set<map_location>& my_villages = ai_.current_team().villages();
950 
951  map_location loc;
952  if(args().size() == 2) {
953  loc = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "is_unowned_village:location")).convert_to<location_callable>()->loc();
954  } else {
955  loc = map_location( args()[1]->evaluate(variables,add_debug_info(fdb,1,"is_unowned_village:x")).as_int(),
956  args()[2]->evaluate(variables,add_debug_info(fdb,2,"is_unowned_village:y")).as_int(), wml_loc());
957  }
958 
959  if(m.is_village(loc) && (my_villages.count(loc)==0) ) {
960  return variant(true);
961  } else {
962  return variant(false);
963  }
964 }
965 
967 {
968  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"unit_moves:unit_location"));
969  std::vector<variant> vars;
970  if(res.is_null()) {
971  return variant(vars);
972  }
973 
974  const map_location& loc = res.convert_to<location_callable>()->loc();
975  const ai::move_map& srcdst = ai_.get_srcdst();
976  typedef ai::move_map::const_iterator Itor;
977  std::pair<Itor,Itor> range = srcdst.equal_range(loc);
978 
979  for(Itor i = range.first; i != range.second; ++i) {
980  vars.emplace_back(std::make_shared<location_callable>(i->second));
981  }
982 
983  return variant(vars);
984 }
985 
986 DEFINE_WFL_FUNCTION(units_can_reach, 2, 2)
987 {
988  std::vector<variant> vars;
989  variant dstsrc_var = args()[0]->evaluate(variables,add_debug_info(fdb,0,"units_can_reach:possible_move_list"));
990  const ai::move_map& dstsrc = dstsrc_var.convert_to<move_map_callable>()->dstsrc();
991  std::pair<ai::move_map::const_iterator,ai::move_map::const_iterator> range =
992  dstsrc.equal_range(args()[1]->evaluate(variables, add_debug_info(fdb, 1, "units_can_reach:possible_move_list")).convert_to<location_callable>()->loc());
993  while(range.first != range.second) {
994  unit_map::const_iterator un = resources::gameboard->units().find(range.first->second);
995  assert(un != resources::gameboard->units().end());
996  vars.emplace_back(std::make_shared<unit_callable>(*un));
997  ++range.first;
998  }
999 
1000  return variant(vars);
1001 }
1002 
1003 DEFINE_FAI_FUNCTION(is_avoided_location, 1, 1)
1004 {
1005  variant res = args()[0]->evaluate(variables,add_debug_info(fdb,0,"is_avoided_location:location"));
1006  if(res.is_null()) {
1007  return variant();
1008  }
1009  const map_location& loc = res.convert_to<location_callable>()->loc();
1010  return variant(ai_.get_avoid().match(loc));
1011 }
1012 
1013 DEFINE_WFL_FUNCTION(max_possible_damage, 2, 2)
1014 {
1015  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage:unit1"));
1016  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage:unit2"));
1017  if(u1.is_null() || u2.is_null()) {
1018  return variant();
1019  }
1020 
1021  unit_adapter u_attacker(u1), u_defender(u2);
1022  int best = 0;
1023  for(const attack_type& atk : u_attacker.attacks()) {
1024  const int dmg = round_damage(atk.damage(), u_defender.damage_from(atk), 100) * atk.num_attacks();
1025  if(dmg > best)
1026  best = dmg;
1027  }
1028  return variant(best);
1029 }
1030 
1031 namespace {
1032  std::pair<int, int> best_melee_and_ranged_attacks(unit_adapter attacker, unit_adapter defender) {
1033  int highest_melee_damage = 0;
1034  int highest_ranged_damage = 0;
1035 
1036  for (const attack_type &attack : attacker.attacks()) {
1037  const int dmg = round_damage(attack.damage(), defender.damage_from(attack), 100) * attack.num_attacks();
1038  if (attack.range() == "melee") {
1039  highest_melee_damage = std::max(highest_melee_damage, dmg);
1040  } else {
1041  highest_ranged_damage = std::max(highest_ranged_damage, dmg);
1042  }
1043  }
1044 
1045  return std::pair(highest_melee_damage, highest_ranged_damage);
1046  }
1047 }
1048 
1049 DEFINE_WFL_FUNCTION(max_possible_damage_with_retaliation, 2, 2)
1050 {
1051  variant u1 = args()[0]->evaluate(variables,add_debug_info(fdb,0,"max_possible_damage_with_retaliation:unit1"));
1052  variant u2 = args()[1]->evaluate(variables,add_debug_info(fdb,1,"max_possible_damage_with_retaliation:unit2"));
1053 
1054  if(u1.is_null() || u2.is_null()) {
1055  return variant();
1056  }
1057 
1058  unit_adapter attacker(u1);
1059  unit_adapter defender(u2);
1060 
1061  // find max damage inflicted by attacker and by defender to the attacker
1062  std::pair<int, int> best_attacker_attacks = best_melee_and_ranged_attacks(attacker, defender);
1063  std::pair<int, int> best_defender_attacks = best_melee_and_ranged_attacks(defender, attacker);
1064 
1065  std::vector<variant> vars;
1066  vars.emplace_back(best_attacker_attacks.first);
1067  vars.emplace_back(best_attacker_attacks.second);
1068  vars.emplace_back(best_defender_attacks.first);
1069  vars.emplace_back(best_defender_attacks.second);
1070 
1071  return variant(vars);
1072 }
1073 
1074 template<typename T>
1075 class ai_formula_function : public formula_function {
1076 protected:
1077  formula_ai& ai_;
1078 public:
1079  ai_formula_function(const std::string& name, ai::formula_ai& ai) : formula_function(name), ai_(ai) {}
1080  function_expression_ptr generate_function_expression(const std::vector<expression_ptr>& args) const {
1081  return std::make_shared<T>(args, ai_);
1082  }
1083 };
1084 
1085 }
1086 
1087 // This macro is for functions taking an additional formula_ai argument.
1088 // Functions using the other macro could potentially be made core.
1089 #define DECLARE_FAI_FUNCTION(name) \
1090  add_function(#name, std::make_shared<ai_formula_function<name##_function>>(#name, ai))
1091 
1094 {
1095  function_symbol_table& functions_table = *this;
1096  DECLARE_WFL_FUNCTION(outcomes);
1097  //DECLARE_FAI_FUNCTION(evaluate_for_position);
1098  DECLARE_WFL_FUNCTION(move);
1099  DECLARE_WFL_FUNCTION(move_partial);
1100  DECLARE_WFL_FUNCTION(attack);
1101  DECLARE_FAI_FUNCTION(rate_action);
1102  DECLARE_WFL_FUNCTION(recall);
1103  DECLARE_WFL_FUNCTION(recruit);
1104  DECLARE_FAI_FUNCTION(is_avoided_location);
1105  DECLARE_WFL_FUNCTION(is_village);
1106  DECLARE_FAI_FUNCTION(is_unowned_village);
1108  DECLARE_WFL_FUNCTION(set_unit_var);
1109  DECLARE_WFL_FUNCTION(fallback);
1110  DECLARE_WFL_FUNCTION(units_can_reach);
1111  DECLARE_FAI_FUNCTION(debug_label);
1112  DECLARE_WFL_FUNCTION(max_possible_damage);
1113  DECLARE_WFL_FUNCTION(max_possible_damage_with_retaliation);
1114  DECLARE_FAI_FUNCTION(next_hop);
1115  DECLARE_WFL_FUNCTION(castle_locs);
1116  DECLARE_WFL_FUNCTION(timeofday_modifier);
1117  DECLARE_FAI_FUNCTION(distance_to_nearest_unowned_village);
1118  DECLARE_FAI_FUNCTION(shortest_path);
1119  DECLARE_FAI_FUNCTION(simplest_path);
1120  DECLARE_FAI_FUNCTION(nearest_keep);
1121  DECLARE_FAI_FUNCTION(suitable_keep);
1122  DECLARE_WFL_FUNCTION(nearest_loc);
1123  DECLARE_FAI_FUNCTION(find_shroud);
1124  DECLARE_FAI_FUNCTION(close_enemies);
1125  DECLARE_WFL_FUNCTION(calculate_outcome);
1126  DECLARE_FAI_FUNCTION(run_file);
1127  DECLARE_FAI_FUNCTION(calculate_map_ownership);
1128 }
1129 #undef DECLARE_WFL_FUNCTION
1130 
1131 }
int combat_modifier(const unit_map &units, const gamemap &map, const map_location &loc, unit_alignments::type 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:1582
double t
Definition: astarsearch.cpp:63
boost::iterator_range< boost::indirect_iterator< attack_list::const_iterator > > const_attack_itors
const wfl::variant & get_keeps_cache() const
Definition: ai.hpp:114
pathfind::teleport_map get_allowed_teleports(unit_map::iterator &unit_it) const
Definition: ai.cpp:248
wfl::formula_ptr create_optional_formula(const std::string &formula_string) const
Create a new formula from the string, using the symbol table which is stored in the AI.
Definition: ai.cpp:133
wfl::variant get_keeps() const
Definition: ai.cpp:616
pathfind::plain_route shortest_path_calculator(const map_location &src, const map_location &dst, unit_map::iterator &unit_it, pathfind::teleport_map &allowed_teleports) const
Definition: ai.cpp:194
virtual const map_location & suitable_keep(const map_location &leader_location, const pathfind::paths &leader_paths) const override
get most suitable keep for leader - nearest free that can be reached in 1 turn, if none - return near...
Definition: contexts.hpp:856
virtual const team & current_team() const override
Definition: contexts.hpp:450
virtual const terrain_filter & get_avoid() const override
Definition: contexts.hpp:586
virtual const move_map & get_srcdst() const override
Definition: contexts.hpp:716
virtual double get_aggression() const override
Definition: contexts.hpp:546
virtual const moves_map & get_possible_moves() const override
Definition: contexts.hpp:676
virtual side_number get_side() const override
Get the side number.
Definition: contexts.hpp:396
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:167
Sort-of-Singleton that many classes, both GUI and non-GUI, use to access the game data.
Definition: display.hpp:81
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:95
Game board class.
Definition: game_board.hpp:46
virtual const unit_map & units() const override
Definition: game_board.hpp:106
virtual const gamemap & map() const override
Definition: game_board.hpp:96
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
Encapsulates the map of the game.
Definition: map.hpp:172
bool is_village(const map_location &loc) const
Definition: map.cpp:65
const std::vector< map_location > & villages() const
Return a list of the locations of villages on the map.
Definition: map.hpp:237
static const int UNREACHABLE
Magic value that signifies a hex is unreachable.
Definition: movetype.hpp:175
int resistance_against(const attack_type &attack) const
Returns the resistance against the indicated attack.
Definition: movetype.hpp:295
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:281
void add_label(const terrain_label *)
Definition: replay.cpp:272
bool is_enemy(int n) const
Definition: team.hpp:229
const std::set< map_location > & villages() const
Definition: team.hpp:170
bool shrouded(const map_location &loc) const
Definition: team.cpp:650
static color_t get_side_color(int side)
Definition: team.cpp:959
To store label data Class implements logic for rendering.
Definition: label.hpp:111
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
std::size_t count(const map_location &loc) const
Definition: map.hpp:413
unit_iterator find(std::size_t id)
Definition: map.cpp:302
unit_iterator begin()
Definition: map.hpp:418
A single unit type that the player may recruit.
Definition: types.hpp:43
const movetype & movement_type() const
Definition: types.hpp:188
const_attack_itors attacks() const
Definition: types.cpp:542
This class represents a single unit of a specific type.
Definition: unit.hpp:133
ai_function_symbol_table(ai::formula_ai &ai)
std::size_t num_elements() const
Definition: variant.cpp:267
@ DECIMAL_VARIANT
Definition: variant.hpp:31
std::shared_ptr< T > convert_to() const
Definition: variant.hpp:100
Default AI contexts.
Declarations for File-IO.
Defines formula ai.
std::size_t i
Definition: function.cpp:968
#define DEFINE_WFL_FUNCTION(name, min_args, max_args)
Helper macro to declare an associated class for a WFL function.
Definition: function.hpp:27
#define DECLARE_WFL_FUNCTION(name)
Declares a function name in the local function table functions_table.
Definition: function.hpp:47
#define ERR_AI
const unit * unit_
formula_ai & ai_
static lg::log_domain log_formula_ai("ai/engine/fai")
int h
map_location loc_
#define LOG_AI
#define WRN_AI
#define DEFINE_FAI_FUNCTION(name, min_args, max_args)
int movement_cost_
const unit_type * unit_type_
const std::vector< node > & nodes
int w
#define DECLARE_FAI_FUNCTION(name)
unsigned in
If equal to search_counter, the node is off the list.
static bool operator<(const placing_info &a, const placing_info &b)
Definition: game_state.cpp:108
#define UNUSED(x)
Definition: global.hpp:31
unit_alignments::type alignment() const
The alignment of this unit.
Definition: unit.hpp:475
int damage_from(const attack_type &attack, bool attacker, const map_location &loc, const_attack_ptr weapon=nullptr) const
Calculates the damage this unit would take from a certain attack.
Definition: unit.hpp:972
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:927
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition: unit.hpp:1440
bool is_fearless() const
Gets whether this unit is fearless - ie, unaffected by time of day.
Definition: unit.hpp:1247
T end(const std::pair< T, T > &p)
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:474
std::size_t distance_between(const map_location &a, const map_location &b)
Function which gives the number of hexes between two tiles (i.e.
Definition: location.cpp:545
Standard logging facilities (interface).
constexpr 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:80
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
std::multimap< map_location, map_location > move_map
The standard way in which a map of possible moves is recorded.
Definition: game_info.hpp:43
std::map< map_location, pathfind::paths > moves_map
The standard way in which a map of possible movement routes to location is recorded.
Definition: game_info.hpp:46
std::string get_wml_location(const std::string &filename, const std::string &current_dir)
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::string path
Definition: filesystem.cpp:83
General purpose widgets.
const std::vector< std::string > items
plain_route a_star_search(const map_location &src, const map_location &dst, double stop_at, const cost_calculator &calc, const std::size_t width, const std::size_t height, const teleport_map *teleports, bool border)
game_board * gameboard
Definition: resources.cpp:20
replay * recorder
Definition: resources.cpp:28
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Definition: contexts.hpp:43
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
std::shared_ptr< formula > formula_ptr
Definition: formula_fwd.hpp:22
std::shared_ptr< function_expression > function_expression_ptr
Definition: function.hpp:172
This module contains various pathfinding functions and utilities.
Replay control code.
static config unit_moves(const reports::context &rc, const unit *u, bool is_visible_unit)
Definition: reports.cpp:667
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59
Encapsulates the map of the game.
Definition: location.hpp:38
bool valid() const
Definition: location.hpp:89
int wml_y() const
Definition: location.hpp:154
static const map_location & null_location()
Definition: location.hpp:81
int wml_x() const
Definition: location.hpp:153
Function which only uses terrain, ignoring shroud, enemies, etc.
Definition: pathfind.hpp:242
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:73
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:133
std::vector< map_location > steps
Definition: pathfind.hpp:135
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
pointer get_shared_ptr() const
This is exactly the same as operator-> but it's slightly more readable, and can replace &*iter syntax...
Definition: map.hpp:217
static map_location::DIRECTION n
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition: unit.cpp:207
#define a
#define b