The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
ai.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Defines formula ai candidate actions - headers
18  */
19 
20 #include "ai/formula/ai.hpp"
21 #include "global.hpp"
22 
23 #include "ai/formula/callable_objects.hpp" // for unit_callable, etc
24 #include "chat_events.hpp" // for chat_handler, etc
25 #include "display_chat_manager.hpp"
26 #include "formula/function.hpp" // for formula_expression
27 #include "game_board.hpp" // for game_board
28 #include "game_display.hpp" // for game_display
29 #include "log.hpp" // for LOG_STREAM, logger, etc
30 #include "map/map.hpp" // for gamemap
31 #include "menu_events.hpp"
32 #include "pathfind/pathfind.hpp" // for plain_route, etc
33 #include "pathfind/teleport.hpp" // for get_teleport_locations, etc
34 #include "recall_list_manager.hpp" // for recall_list_manager
35 #include "resources.hpp" // for gameboard, teams, units, etc
36 #include "serialization/string_utils.hpp" // for split
37 #include "team.hpp" // for team
38 #include "terrain/filter.hpp" // for terrain_filter
39 #include "time_of_day.hpp" // for time_of_day
40 #include "tod_manager.hpp" // for tod_manager
41 #include "tstring.hpp" // for t_string, operator+
42 #include "units/unit.hpp" // for unit
43 #include "units/formula_manager.hpp" // for unit_formula_manager
44 #include "units/ptr.hpp" // for unit_ptr
45 #include "units/types.hpp"
46 #include "formula/formula.hpp" // for formula_error, formula, etc
47 #include "map/location.hpp" // for map_location, etc
48 #include "ai/actions.hpp" // for recall_result, etc
49 #include "ai/manager.hpp" // for manager
51 #include "ai/composite/stage.hpp" // for stage
52 #include "ai/default/contexts.hpp" // for attack_analysis
53 #include "ai/formula/function_table.hpp" // for ai_function_symbol_table
54 #include "ai/game_info.hpp" // for move_result_ptr, move_map, etc
55 #include "candidates.hpp" // for base_candidate_action, etc
56 
57 #include <boost/intrusive_ptr.hpp> // for intrusive_ptr
58 #include <boost/lexical_cast.hpp> // for lexical_cast
59 #include <boost/shared_ptr.hpp> // for shared_ptr
60 #include <cassert> // for assert
61 #include <ctime> // for time
62 #include <map> // for multimap<>::const_iterator, etc
63 #include <sstream> // for operator<<, basic_ostream, etc
64 #include <stack> // for stack
65 #include <vector> // for vector, allocator, etc
66 
67 static lg::log_domain log_formula_ai("ai/engine/fai");
68 #define DBG_AI LOG_STREAM(debug, log_formula_ai)
69 #define LOG_AI LOG_STREAM(info, log_formula_ai)
70 #define WRN_AI LOG_STREAM(warn, log_formula_ai)
71 #define ERR_AI LOG_STREAM(err, log_formula_ai)
72 
73 
74 using namespace game_logic;
75 
76 namespace ai {
77 
79 
81 {
82  ca_ptr new_ca;
83  const t_string &name = rc_action["name"];
84  try {
85  const t_string &type = rc_action["type"];
86 
87  if( type == "movement") {
88  new_ca = ca_ptr(new move_candidate_action(name, type, rc_action, &function_table_));
89  } else if( type == "attack") {
90  new_ca = ca_ptr(new attack_candidate_action(name, type, rc_action, &function_table_));
91  } else {
92  ERR_AI << "Unknown candidate action type: " << type << std::endl;
93  }
94  } catch(formula_error& e) {
95  handle_exception(e, "Error while registering candidate action '" + name + "'");
96  }
97  return new_ca;
98 }
99 
101  return recursion_counter_.get_count();
102 }
103 
104 
106  :
109  ai_ptr_(nullptr),
110  cfg_(cfg),
111  recursion_counter_(context.get_recursion_count()),
112  keeps_cache_(),
113  infinite_loop_guardian_(),
114  vars_(),
115  function_table_(*this)
116 {
117  add_ref();
119  LOG_AI << "creating new formula ai"<< std::endl;
120 }
121 
123 {
124  handle_exception(e, "Error while parsing formula");
125 }
126 
128 {
129  LOG_AI << failed_operation << ": " << e.formula << std::endl;
130  display_message(failed_operation + ": " + e.formula);
131  //if line number = 0, don't display info about filename and line number
132  if (e.line != 0) {
133  LOG_AI << e.type << " in " << e.filename << ":" << e.line << std::endl;
134  display_message(e.type + " in " + e.filename + ":" + std::to_string(e.line));
135  } else {
136  LOG_AI << e.type << std::endl;
138  }
139 }
140 
142 {
143  resources::screen->get_chat_manager().add_chat_message(time(nullptr), "wfl", get_side(), msg,
145 
146 }
147 
149  try{
151  }
152  catch(formula_error& e) {
153  handle_exception(e);
154  return game_logic::formula_ptr();
155  }
156 }
157 
158 
160 {
161  ai_ptr_ = context;
162 }
163 
164 
166 {
167  try{
168 
169  game_logic::formula f(formula_str, &function_table_);
170 
171  game_logic::map_formula_callable callable(this);
172  callable.add_ref();
173 
174  //formula_debugger fdb;
175  const variant v = f.evaluate(callable,nullptr);
176 
177  if (ai_ptr_) {
178  variant var = execute_variant(v, *ai_ptr_, true );
179 
180  if ( !var.is_empty() ) {
181  return "Made move: " + var.to_debug_string();
182  }
183  }
184 
185  return v.to_debug_string();
186  }
187  catch(formula_error& e) {
188  e.line = 0;
189  handle_exception(e);
190  throw;
191  }
192 }
193 
195 {
196  if (!formula_) {
197  throw formula_error("null formula passed to make_action","","formula",0);
198  }
199  LOG_AI << "do move...\n";
200  const variant var = formula_->evaluate(variables);///@todo 1.9 add formula_debugger
201  variant res;
202 
203  if (ai_ptr_) {
204  res = execute_variant(var, *ai_ptr_, false);
205  } else {
206  ERR_AI << "skipped execution of action because ai context is not set correctly" << std::endl;
207  }
208 
209  return res;
210 }
211 
213  const map_location &dst, unit_map::iterator &unit_it,
214  pathfind::teleport_map& allowed_teleports) const
215 {
216  map_location destination = dst;
217 
220 
221  unit_map::const_iterator dst_un = units_.find(destination);
222 
224 
225  if( dst_un != units_.end() ) {
226  //there is unit standing at dst, let's try to find free hex to move to
227  const map_location::DIRECTION preferred = destination.get_relative_dir(src);
228 
229  int best_rating = 100;//smaller is better
230  map_location adj[6];
231  get_adjacent_tiles(destination,adj);
232 
233  for(size_t n = 0; n != 6; ++n) {
234  if(resources::gameboard->map().on_board(adj[n]) == false) {
235  continue;
236  }
237 
238  if(units_.find(adj[n]) != units_.end()) {
239  continue;
240  }
241 
242  static const size_t NDIRECTIONS = map_location::NDIRECTIONS;
243  unsigned int difference = abs(int(preferred - n));
244  if(difference > NDIRECTIONS/2) {
245  difference = NDIRECTIONS - difference;
246  }
247 
248  const int rating = difference * 2;
249  if(rating < best_rating || res.valid() == false) {
250  best_rating = rating;
251  res = adj[n];
252  }
253  }
254  }
255 
256  if( res != map_location() ) {
257  destination = res;
258  }
259 
260  pathfind::plain_route route = pathfind::a_star_search(src, destination, 1000.0, &calc,
261  resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports);
262 
263  return route;
264 }
265 
267 {
268  return pathfind::get_teleport_locations(*unit_it, current_team(), true);
269 }
270 
271 //commandline=true when we evaluate formula from commandline, false otherwise (default)
273 {
274  std::stack<variant> vars;
275  if(var.is_list()) {
276  for(size_t n = 1; n <= var.num_elements() ; ++n) {
277  vars.push(var[ var.num_elements() - n ]);
278  }
279  } else {
280  vars.push(var);
281  }
282 
283  std::vector<variant> made_moves;
284 
285  variant error;
286 
288 
289  while( !vars.empty() ) {
290 
291  if(vars.top().is_null()) {
292  vars.pop();
293  continue;
294  }
295 
296  variant action = vars.top();
297  vars.pop();
298 
299  game_logic::safe_call_callable* safe_call = try_convert_variant<game_logic::safe_call_callable>(action);
300 
301  if(safe_call) {
302  action = safe_call->get_main();
303  }
304 
305  const move_callable* move = try_convert_variant<move_callable>(action);
306  const move_partial_callable* move_partial = try_convert_variant<move_partial_callable>(action);
307  const attack_callable* attack = try_convert_variant<attack_callable>(action);
308  const attack_analysis* _attack_analysis = try_convert_variant<attack_analysis>(action);
309  const recruit_callable* recruit_command = try_convert_variant<recruit_callable>(action);
310  const recall_callable* recall_command = try_convert_variant<recall_callable>(action);
311  const set_var_callable* set_var_command = try_convert_variant<set_var_callable>(action);
312  const set_unit_var_callable* set_unit_var_command = try_convert_variant<set_unit_var_callable>(action);
313  const fallback_callable* fallback_command = try_convert_variant<fallback_callable>(action);
314 
315  if( move || move_partial ) {
317 
318  if(move)
319  move_result = ai_.execute_move_action(move->src(), move->dst(), true);
320  else
321  move_result = ai_.execute_move_action(move_partial->src(), move_partial->dst(), false);
322 
323  if ( !move_result->is_ok() ) {
324  if( move ) {
325  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'move' formula function\n" << std::endl;
326 
327  if(safe_call) {
328  //safe_call was called, prepare error information
329  error = variant(new safe_call_result(move,
330  move_result->get_status(), move_result->get_unit_location()));
331  }
332  } else {
333  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'move_partial' formula function\n" << std::endl;
334 
335  if(safe_call) {
336  //safe_call was called, prepare error information
337  error = variant(new safe_call_result(move_partial,
338  move_result->get_status(), move_result->get_unit_location()));
339  }
340  }
341  }
342 
343  if( move_result->is_gamestate_changed() )
344  made_moves.push_back(action);
345  } else if(attack) {
346  bool gamestate_changed = false;
348 
349  if( attack->move_from() != attack->src() ) {
350  move_result = ai_.execute_move_action(attack->move_from(), attack->src(), false);
351  gamestate_changed |= move_result->is_gamestate_changed();
352 
353  if (!move_result->is_ok()) {
354  //move part failed
355  LOG_AI << "ERROR #" << move_result->get_status() << " while executing 'attack' formula function\n" << std::endl;
356 
357  if(safe_call) {
358  //safe_call was called, prepare error information
359  error = variant(new safe_call_result(attack,
360  move_result->get_status(), move_result->get_unit_location()));
361  }
362  }
363  }
364 
365  if (!move_result || move_result->is_ok() ) {
366  //if move wasn't done at all or was done successfully
367  attack_result_ptr attack_result = ai_.execute_attack_action(attack->src(), attack->dst(), attack->weapon() );
368  gamestate_changed |= attack_result->is_gamestate_changed();
369  if (!attack_result->is_ok()) {
370  //attack failed
371 
372  LOG_AI << "ERROR #" << attack_result->get_status() << " while executing 'attack' formula function\n" << std::endl;
373 
374  if(safe_call) {
375  //safe_call was called, prepare error information
376  error = variant(new safe_call_result(attack, attack_result->get_status()));
377  }
378  }
379  }
380 
381  if (gamestate_changed) {
382  made_moves.push_back(action);
383  }
384  } else if(_attack_analysis) {
385  //If we get an attack analysis back we will do the first attack.
386  //Then the AI can get run again and re-choose.
387  assert(_attack_analysis->movements.empty() == false);
388 
389  //make sure that unit which has to attack is at given position and is able to attack
390  unit_map::const_iterator unit = units.find(_attack_analysis->movements.front().first);
391  if (!unit.valid() || unit->attacks_left() == 0)
392  continue;
393 
394  const map_location& move_from = _attack_analysis->movements.front().first;
395  const map_location& att_src = _attack_analysis->movements.front().second;
396  const map_location& att_dst = _attack_analysis->target;
397 
398  //check if target is still valid
399  unit = units.find(att_dst);
400  if ( unit == units.end() )
401  continue;
402 
403  //check if we need to move
404  if( move_from != att_src ) {
405  //now check if location to which we want to move is still unoccupied
406  unit = units.find(att_src);
407  if ( unit != units.end() ) {
408  continue;
409  }
410 
411  ai_.execute_move_action(move_from, att_src);
412  }
413 
414  if(units.count(att_src)) {
415  ai_.execute_attack_action(_attack_analysis->movements.front().second,_attack_analysis->target,-1);
416  }
417  made_moves.push_back(action);
418  } else if(recall_command) {
419 
420  recall_result_ptr recall_result = ai_.check_recall_action(recall_command->id(), recall_command->loc());
421 
422  if( recall_result->is_ok() ) {
423  recall_result->execute();
424  }
425 
426  if (!recall_result->is_ok()) {
427 
428  if(safe_call) {
429  //safe call was called, prepare error information
430  error = variant(new safe_call_result(recall_command,
431  recall_result->get_status()));
432 
433  LOG_AI << "ERROR #" <<recall_result->get_status() << " while executing 'recall' formula function\n"<<std::endl;
434  } else {
435  ERR_AI << "ERROR #" <<recall_result->get_status() << " while executing 'recall' formula function\n"<<std::endl;
436  }
437  }
438 
439  if( recall_result->is_gamestate_changed() ) {
440  made_moves.push_back(action);
441  }
442 
443  } else if(recruit_command) {
444  recruit_result_ptr recruit_result = ai_.check_recruit_action(recruit_command->type(), recruit_command->loc());
445 
446  //is_ok()==true means that the action is successful (eg. no unexpected events)
447  //is_ok() must be checked or the code will complain :)
448  if( recruit_result->is_ok() )
449  recruit_result->execute();
450 
451  if (!recruit_result->is_ok()) {
452 
453  if(safe_call) {
454  //safe call was called, prepare error information
455  error = variant(new safe_call_result(recruit_command,
456  recruit_result->get_status()));
457 
458  LOG_AI << "ERROR #" <<recruit_result->get_status() << " while executing 'recruit' formula function\n"<<std::endl;
459  } else {
460  ERR_AI << "ERROR #" <<recruit_result->get_status() << " while executing 'recruit' formula function\n"<<std::endl;
461  }
462  }
463 
464  //is_gamestate_changed()==true means that the game state was somehow changed by action.
465  //it is believed that during a turn, a game state can change only a finite number of times
466  if( recruit_result->is_gamestate_changed() )
467  made_moves.push_back(action);
468 
469  } else if(set_var_command) {
471  LOG_AI << "Setting variable: " << set_var_command->key() << " -> " << set_var_command->value().to_debug_string() << "\n";
472  vars_.add(set_var_command->key(), set_var_command->value());
473  made_moves.push_back(action);
474  } else {
475  //too many calls in a row - possible infinite loop
476  ERR_AI << "ERROR #" << 5001 << " while executing 'set_var' formula function" << std::endl;
477 
478  if( safe_call )
479  error = variant(new safe_call_result(set_var_command, 5001));
480  }
481  } else if(set_unit_var_command) {
482  int status = 0;
484 
486  status = 5001; //exceeded nmber of calls in a row - possible infinite loop
487  } else if( (unit = units.find(set_unit_var_command->loc())) == units.end() ) {
488  status = 5002; //unit not found
489  } else if (unit->side() != get_side()) {
490  status = 5003;//unit does not belong to our side
491  }
492 
493  if( status == 0 ){
494  LOG_AI << "Setting unit variable: " << set_unit_var_command->key() << " -> " << set_unit_var_command->value().to_debug_string() << "\n";
495  unit->formula_manager().add_formula_var(set_unit_var_command->key(), set_unit_var_command->value());
496  made_moves.push_back(action);
497  } else {
498  ERR_AI << "ERROR #" << status << " while executing 'set_unit_var' formula function" << std::endl;
499  if(safe_call)
500  error = variant(new safe_call_result(set_unit_var_command,
501  status));
502  }
503 
504  } else if( action.is_string() && action.as_string() == "continue") {
506  made_moves.push_back(action);
507  } else {
508  //too many calls in a row - possible infinite loop
509  ERR_AI << "ERROR #" << 5001 << " while executing 'continue' formula keyword" << std::endl;
510 
511  if( safe_call )
512  error = variant(new safe_call_result(nullptr, 5001));
513  }
514  } else if( action.is_string() && (action.as_string() == "end_turn" || action.as_string() == "end" ) ) {
515  return variant();
516  } else if(fallback_command) {
518  //we want give control of the side to human for the rest of this turn
520  }
521  return variant();
522  } else {
523  //this information is unneded when evaluating formulas form commandline
524  if (!commandline) {
525  ERR_AI << "UNRECOGNIZED MOVE: " << action.to_debug_string() << std::endl;
526  }
527  }
528 
529  if( safe_call && (error != variant() || made_moves.empty() || made_moves.back() != action) ){
530  /*if we have safe_call formula and either error occurred, or current action
531  *was not reckognized, then evaluate backup formula from safe_call and execute it
532  *during the next loop
533  */
534 
535  game_logic::map_formula_callable callable(this);
536  callable.add_ref();
537 
538  if(error != variant())
539  callable.add("error", error);
540 
541  variant backup_result = safe_call->get_backup()->evaluate(callable);
542 
543  if(backup_result.is_list()) {
544  for(size_t n = 1; n <= backup_result.num_elements() ; ++n) {
545  vars.push(backup_result[ backup_result.num_elements() - n ]);
546  }
547  } else {
548  vars.push(backup_result);
549  }
550 
551  //store the result in safe_call_callable case we would like to display it to the user
552  //for example if this formula was executed from commandline
553  safe_call->set_backup_result(backup_result);
554 
555  error = variant();
556  }
557  }
558 
559  return variant(&made_moves);
560 }
561 
562 void formula_ai::add_formula_function(const std::string& name, const_formula_ptr formula, const_formula_ptr precondition, const std::vector<std::string>& args)
563 {
564  formula_function_ptr fcn(new user_formula_function(name,formula,precondition,args));
565  function_table_.add_function(name, fcn);
566 }
567 
568 namespace {
569 template<typename Container>
570 variant villages_from_set(const Container& villages,
571  const std::set<map_location>* exclude=nullptr) {
572  std::vector<variant> vars;
573  for(const map_location& loc : villages) {
574  if(exclude && exclude->count(loc)) {
575  continue;
576  }
577  vars.push_back(variant(new location_callable(loc)));
578  }
579 
580  return variant(&vars);
581 }
582 }
583 
585 {
586  const unit_map& units = *resources::units;
587 
588  if(key == "aggression")
589  {
591 
592  } else if(key == "attack_depth")
593  {
594  return variant(get_attack_depth());
595 
596  } else if(key == "avoid")
597  {
598  std::set<map_location> av_locs;
599  get_avoid().get_locations(av_locs);
600  return villages_from_set(av_locs);
601 
602  } else if(key == "caution")
603  {
605 
606  } else if(key == "grouping")
607  {
608  return variant(get_grouping());
609 
610  } else if(key == "leader_aggression")
611  {
613 
614  } else if(key == "leader_ignores_keep")
615  {
617 
618  } else if(key == "leader_value")
619  {
621 
622  } else if(key == "passive_leader")
623  {
624  return variant(get_passive_leader());
625 
626  } else if(key == "passive_leader_shares_keep")
627  {
629 
630  } else if(key == "recruitment_pattern")
631  {
632  const std::vector<std::string> &rp = get_recruitment_pattern();
633  std::vector<variant> vars;
634  for(const std::string &i : rp) {
635  vars.push_back(variant(i));
636  }
637  return variant(&vars);
638 
639  } else if(key == "scout_village_targeting")
640  {
642 
643  } else if(key == "support_villages")
644  {
645  return variant(get_support_villages());
646 
647  } else if(key == "village_value")
648  {
650 
651  } else if(key == "villages_per_scout")
652  {
654 
655  } else if(key == "attacks")
656  {
657  return get_attacks_as_variant();
658 
659  } else if(key == "turn")
660  {
661  return variant(resources::tod_manager->turn());
662 
663  } else if(key == "time_of_day")
664  {
665  return variant(resources::tod_manager->get_time_of_day().id);
666 
667  } else if(key == "my_side")
668  {
669  return variant(new team_callable((*resources::teams)[get_side()-1]));
670 
671  } else if(key == "my_side_number")
672  {
673  return variant(get_side()-1);
674 
675  } else if(key == "teams")
676  {
677  std::vector<variant> vars;
678  for(std::vector<team>::const_iterator i = resources::teams->begin(); i != resources::teams->end(); ++i) {
679  vars.push_back(variant(new team_callable(*i)));
680  }
681  return variant(&vars);
682 
683  } else if(key == "allies")
684  {
685  std::vector<variant> vars;
686  for( size_t i = 0; i < resources::teams->size(); ++i) {
687  if ( !current_team().is_enemy( i+1 ) )
688  vars.push_back(variant( i ));
689  }
690  return variant(&vars);
691 
692  } else if(key == "enemies")
693  {
694  std::vector<variant> vars;
695  for( size_t i = 0; i < resources::teams->size(); ++i) {
696  if ( current_team().is_enemy( i+1 ) )
697  vars.push_back(variant( i ));
698  }
699  return variant(&vars);
700 
701  } else if(key == "my_recruits")
702  {
703  std::vector<variant> vars;
704 
706 
707  const std::set<std::string>& recruits = current_team().recruits();
708  if(recruits.empty()) {
709  return variant( &vars );
710  }
711  for(std::set<std::string>::const_iterator i = recruits.begin(); i != recruits.end(); ++i)
712  {
713  const unit_type *ut = unit_types.find(*i);
714  if (ut)
715  {
716  vars.push_back(variant(new unit_type_callable(*ut)));
717  }
718  }
719  return variant( &vars );
720 
721  } else if(key == "recruits_of_side")
722  {
723  std::vector<variant> vars;
724  std::vector< std::vector< variant> > tmp;
725 
727 
728  for( size_t i = 0; i<resources::teams->size(); ++i)
729  {
730  std::vector<variant> v;
731  tmp.push_back( v );
732 
733  const std::set<std::string>& recruits = (*resources::teams)[i].recruits();
734  if(recruits.empty()) {
735  continue;
736  }
737  for(std::set<std::string>::const_iterator str_it = recruits.begin(); str_it != recruits.end(); ++str_it)
738  {
739  const unit_type *ut = unit_types.find(*str_it);
740  if (ut)
741  {
742  tmp[i].push_back(variant(new unit_type_callable(*ut)));
743  }
744  }
745  }
746 
747  for( size_t i = 0; i<tmp.size(); ++i)
748  vars.push_back( variant( &tmp[i] ));
749  return variant(&vars);
750 
751  } else if(key == "units")
752  {
753  std::vector<variant> vars;
754  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
755  vars.push_back(variant(new unit_callable(*i)));
756  }
757  return variant(&vars);
758 
759  } else if(key == "units_of_side")
760  {
761  std::vector<variant> vars;
762  std::vector< std::vector< variant> > tmp;
763  for( size_t i = 0; i<resources::teams->size(); ++i)
764  {
765  std::vector<variant> v;
766  tmp.push_back( v );
767  }
768  for(const unit &u : units) {
769  tmp[u.side() - 1].push_back(variant(new unit_callable(u)));
770  }
771  for( size_t i = 0; i<tmp.size(); ++i)
772  vars.push_back( variant( &tmp[i] ));
773  return variant(&vars);
774 
775  } else if(key == "my_units")
776  {
777  std::vector<variant> vars;
778  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
779  if (i->side() == get_side()) {
780  vars.push_back(variant(new unit_callable(*i)));
781  }
782  }
783  return variant(&vars);
784 
785  } else if(key == "enemy_units")
786  {
787  std::vector<variant> vars;
788  for(unit_map::const_iterator i = units.begin(); i != units.end(); ++i) {
789  if (current_team().is_enemy(i->side())) {
790  if (!i->incapacitated()) {
791  vars.push_back(variant(new unit_callable(*i)));
792  }
793  }
794  }
795  return variant(&vars);
796 
797  } else if(key == "my_moves")
798  {
799  return variant(new move_map_callable(get_srcdst(), get_dstsrc(), units));
800 
801  } else if(key == "my_attacks")
802  {
803  return variant(new attack_map_callable(*this, units));
804  } else if(key == "enemy_moves")
805  {
807 
808  } else if(key == "my_leader")
809  {
811  if(i == units.end()) {
812  return variant();
813  }
814  return variant(new unit_callable(*i));
815 
816  } else if(key == "recall_list")
817  {
818  std::vector<variant> tmp;
819 
820  for(std::vector<unit_ptr >::const_iterator i = current_team().recall_list().begin(); i != current_team().recall_list().end(); ++i) {
821  tmp.push_back( variant( new unit_callable(**i) ) );
822  }
823 
824  return variant( &tmp );
825 
826  } else if(key == "vars")
827  {
828  return variant(&vars_);
829  } else if(key == "keeps")
830  {
831  return get_keeps();
832  } else if(key == "map")
833  {
834  return variant(new gamemap_callable(resources::gameboard->map()));
835  } else if(key == "villages")
836  {
837  return villages_from_set(resources::gameboard->map().villages());
838  } else if(key == "villages_of_side")
839  {
840  std::vector<variant> vars;
841  for(size_t i = 0; i<resources::teams->size(); ++i)
842  {
843  vars.push_back( variant() );
844  }
845  for(size_t i = 0; i<vars.size(); ++i)
846  {
847  vars[i] = villages_from_set((*resources::teams)[i].villages());
848  }
849  return variant(&vars);
850 
851  } else if(key == "my_villages")
852  {
853  return villages_from_set(current_team().villages());
854 
855  } else if(key == "enemy_and_unowned_villages")
856  {
857  return villages_from_set(resources::gameboard->map().villages(), &current_team().villages());
858  }
859 
860  return variant();
861 }
862 
863 void formula_ai::get_inputs(std::vector<formula_input>* inputs) const
864 {
866  inputs->push_back(game_logic::formula_input("aggression", FORMULA_READ_ONLY));
867  inputs->push_back(game_logic::formula_input("leader_aggression", FORMULA_READ_ONLY));
868  inputs->push_back(game_logic::formula_input("caution", FORMULA_READ_ONLY));
869  inputs->push_back(game_logic::formula_input("attacks", FORMULA_READ_ONLY));
870  inputs->push_back(game_logic::formula_input("my_side", FORMULA_READ_ONLY));
871  inputs->push_back(game_logic::formula_input("teams", FORMULA_READ_ONLY));
872  inputs->push_back(game_logic::formula_input("turn", FORMULA_READ_ONLY));
873  inputs->push_back(game_logic::formula_input("time_of_day", FORMULA_READ_ONLY));
874  inputs->push_back(game_logic::formula_input("keeps", FORMULA_READ_ONLY));
875  inputs->push_back(game_logic::formula_input("vars", FORMULA_READ_ONLY));
876  inputs->push_back(game_logic::formula_input("allies", FORMULA_READ_ONLY));
877  inputs->push_back(game_logic::formula_input("enemies", FORMULA_READ_ONLY));
878  inputs->push_back(game_logic::formula_input("map", FORMULA_READ_ONLY));
879  inputs->push_back(game_logic::formula_input("my_attacks", FORMULA_READ_ONLY));
880  inputs->push_back(game_logic::formula_input("enemy_moves", FORMULA_READ_ONLY));
881  inputs->push_back(game_logic::formula_input("my_leader", FORMULA_READ_ONLY));
882  inputs->push_back(game_logic::formula_input("my_recruits", FORMULA_READ_ONLY));
883  //inputs->push_back(game_logic::formula_input("recall_list", FORMULA_READ_ONLY));
884  inputs->push_back(game_logic::formula_input("recruits_of_side", FORMULA_READ_ONLY));
885  inputs->push_back(game_logic::formula_input("units", FORMULA_READ_ONLY));
886  inputs->push_back(game_logic::formula_input("units_of_side", FORMULA_READ_ONLY));
887  inputs->push_back(game_logic::formula_input("my_units", FORMULA_READ_ONLY));
888  inputs->push_back(game_logic::formula_input("enemy_units", FORMULA_READ_ONLY));
889  inputs->push_back(game_logic::formula_input("villages", FORMULA_READ_ONLY));
890  inputs->push_back(game_logic::formula_input("my_villages", FORMULA_READ_ONLY));
891  inputs->push_back(game_logic::formula_input("villages_of_side", FORMULA_READ_ONLY));
892  inputs->push_back(game_logic::formula_input("enemy_and_unowned_villages", FORMULA_READ_ONLY));
893 }
894 
896 {
897  if(keeps_cache_.is_null()) {
898  std::vector<variant> vars;
899  for(size_t x = 0; x != size_t(resources::gameboard->map().w()); ++x) {
900  for(size_t y = 0; y != size_t(resources::gameboard->map().h()); ++y) {
901  const map_location loc(x,y);
902  if(resources::gameboard->map().is_keep(loc)) {
903  map_location adj[6];
904  get_adjacent_tiles(loc,adj);
905  for(size_t n = 0; n != 6; ++n) {
906  if(resources::gameboard->map().is_castle(adj[n])) {
907  vars.push_back(variant(new location_callable(loc)));
908  break;
909  }
910  }
911  }
912  }
913  }
914  keeps_cache_ = variant(&vars);
915  }
916 
917  return keeps_cache_;
918 }
919 
921  if (tiles_adjacent(unit_A,unit_B)) {
922  return true;
923  }
924  move_map::const_iterator i;
925  std::pair<move_map::const_iterator,
926  move_map::const_iterator> unit_moves;
927 
928  unit_moves = get_srcdst().equal_range(unit_A);
929  for(i = unit_moves.first; i != unit_moves.second; ++i) {
930  if (tiles_adjacent((*i).second,unit_B)) {
931  return true;
932  }
933  }
934  return false;
935 }
936 
938  //make sure we don't run out of refcount
939  vars_.add_ref();
940 
941  for(const config &func : cfg_.child_range("function"))
942  {
943  const t_string &name = func["name"];
944  const t_string &inputs = func["inputs"];
945  const t_string &formula_str = func["formula"];
946 
947  std::vector<std::string> args = utils::split(inputs);
948  try {
950  create_optional_formula(formula_str),
951  create_optional_formula(func["precondition"]),
952  args);
953  }
954  catch(game_logic::formula_error& e) {
955  handle_exception(e, "Error while registering function '" + name + "'");
956  }
957  }
958 
959 
961  if (const config &ai_vars = cfg_.child("vars"))
962  {
963  variant var;
964  for(const config::attribute &i : ai_vars.attribute_range()) {
965  var.serialize_from_string(i.second);
966  vars_.add(i.first, var);
967  }
968  }
969 
970 
971 }
972 
973 
975 {
976  fai_ca->evaluate(this,*resources::units);
977 
978 }
979 
981 {
982  game_logic::map_formula_callable callable(this);
983  callable.add_ref();
984  fai_ca->update_callable_map( callable );
985  const_formula_ptr move_formula(fai_ca->get_action());
986  return !make_action(move_formula, callable).is_empty();
987 }
988 
990  set_var_counter_(), set_unit_var_counter_(), continue_counter_()
991 {
993 }
994 
997 }
998 
1000  set_var_counter_ = 0;
1001  set_unit_var_counter_ = 0;
1002  continue_counter_ = 0;
1003 }
1004 
1005 //return false if number of calls exceeded MAX_CALLS
1007  if(set_var_counter_ >= MAX_CALLS)
1008  return false;
1009 
1010  set_var_counter_++;
1011  return true;
1012 }
1013 
1015  if(set_unit_var_counter_ >= MAX_CALLS)
1016  return false;
1017 
1018  set_unit_var_counter_++;
1019  return true;
1020 }
1021 
1023  if(continue_counter_ >= MAX_CALLS)
1024  return false;
1025 
1026  continue_counter_++;
1027  return true;
1028 }
1029 
1031 {
1032  if (!cfg_)
1033  {
1034  return config();
1035  }
1036  DBG_AI << "formula_ai::to_config(): "<< cfg_<<std::endl;
1037  config cfg = cfg_;
1038 
1039  //formula AI variables
1040  cfg.clear_children("vars");
1041  if (vars_.empty() == false) {
1042  config &ai_vars = cfg.add_child("vars");
1043 
1044  std::string str;
1046  {
1047  try {
1048  i->second.serialize_to_string(str);
1049  } catch (type_error&) {
1050  WRN_AI << "variable ["<< i->first <<"] is not serializable - it will not be persisted across savegames"<<std::endl;
1051  continue;
1052  }
1053  if (!str.empty())
1054  {
1055  ai_vars[i->first] = str;
1056  str.clear();
1057  }
1058  }
1059  }
1060 
1061  return cfg;
1062 }
1063 
1064 } // end of namespace ai
Defines formula ai.
const expression_ptr & get_backup() const
bool execute_candidate_action(game_logic::candidate_action_ptr fai_ca)
Execute the fai candidate action.
Definition: ai.cpp:980
virtual side_number get_side() const
Get the side number.
Definition: contexts.hpp:480
size_t num_elements() const
Definition: variant.cpp:526
child_itors child_range(const std::string &key)
Definition: config.cpp:613
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)
::tod_manager * tod_manager
Definition: resources.cpp:31
unit_iterator end()
Definition: map.hpp:311
game_logic::formula_ptr create_optional_formula(const std::string &formula_string)
Create a new formula from the string, using the symbol table which is stored in the AI...
Definition: ai.cpp:148
int get_recursion_count() const
Get the value of the recursion counter.
Definition: ai.cpp:100
boost::shared_ptr< formula > formula_ptr
Definition: formula_fwd.hpp:24
Defines formula ai candidate actions - headers.
gamestate_change_observer infinite_loop_guardian_
Definition: ai.hpp:163
Definition: unit.hpp:95
virtual double get_caution() const
Definition: contexts.hpp:697
virtual recall_result_ptr check_recall_action(const std::string &id, const map_location &where=map_location::null_location(), const map_location &from=map_location::null_location())=0
void add_function(const std::string &name, formula_function_ptr fcn)
Definition: function.cpp:1531
size_t count(const map_location &loc) const
Definition: map.hpp:306
const std::set< std::string > & recruits() const
Definition: team.hpp:228
unit_iterator find_leader(int side)
Definition: map.cpp:297
const config cfg_
Definition: ai.hpp:154
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
Managing the AI-Game interaction - AI actions and their results.
virtual double get_leader_value() const
Definition: contexts.hpp:782
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
static lg::log_domain log_formula_ai("ai/engine/fai")
game_display * screen
Definition: resources.cpp:27
bool is_enemy(int n) const
Definition: team.hpp:247
game_logic::candidate_action_ptr ca_ptr
Definition: ai.cpp:78
attribute_map::value_type attribute
Definition: config.hpp:393
const variant & get_main() const
virtual std::string get_grouping() const
Definition: contexts.hpp:745
unit_iterator begin()
Definition: map.hpp:308
void build_all(unit_type::BUILD_STATUS status)
Makes sure the all unit_types are built to the specified level.
Definition: types.cpp:1196
boost::shared_ptr< game_logic::base_candidate_action > candidate_action_ptr
Definition: candidates.hpp:37
Composite AI stages.
unit_type_data unit_types
Definition: types.cpp:1314
std::string evaluate(const std::string &formula_str)
Definition: ai.cpp:165
#define h
DIRECTION get_relative_dir(const map_location &loc, map_location::RELATIVE_DIR_MODE mode) const
Definition: location.cpp:220
game_logic::ai_function_symbol_table function_table_
Definition: ai.hpp:165
GLint GLint GLint GLint GLint GLint y
Definition: glew.h:1220
GLenum src
Definition: glew.h:2392
void clear()
Definition: config.cpp:1055
virtual int get_villages_per_scout() const
Definition: contexts.hpp:884
-file sdl_utils.hpp
void clear_children(const std::string &key)
Definition: config.cpp:820
#define WRN_AI
Definition: ai.cpp:70
bool is_null() const
Definition: variant.hpp:80
virtual double get_leader_aggression() const
Definition: contexts.hpp:763
virtual variant get_value(const std::string &key) const
Definition: ai.cpp:584
const formula_ai & ai_
static const int MAX_COUNTER_VALUE
Definition: contexts.hpp:86
virtual double get_scout_village_targeting() const
Definition: contexts.hpp:860
void on_create()
Definition: ai.cpp:937
virtual void add_formula_function(const std::string &name, game_logic::const_formula_ptr formula, game_logic::const_formula_ptr precondition, const std::vector< std::string > &args)
Definition: ai.cpp:562
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:57
static void remove_gamestate_observer(events::observer *event_observer)
Removes an observer of game events except ai_user_interact event and ai_sync_network event...
Definition: manager.cpp:378
variant make_action(game_logic::const_formula_ptr formula_, const game_logic::formula_callable &variables)
Definition: ai.cpp:194
virtual double get_aggression() const
Definition: contexts.hpp:641
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.hpp:314
std::vector< team > * teams
Definition: resources.cpp:29
void evaluate_candidate_action(game_logic::candidate_action_ptr fai_ca)
Evaluate the fai candidate action.
Definition: ai.cpp:974
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:132
bool valid() const
Definition: location.hpp:69
virtual int get_attack_depth() const
Definition: contexts.hpp:647
bool is_list() const
Definition: variant.hpp:92
static formula_ptr create_optional_formula(const std::string &str, function_symbol_table *symbols=nullptr)
Definition: formula.cpp:1210
variant get_keeps() const
Definition: ai.cpp:895
GLubyte GLubyte GLubyte GLubyte w
Definition: glew.h:1858
const GLdouble * v
Definition: glew.h:1359
GLenum GLenum dst
Definition: glew.h:2392
game_board * gameboard
Definition: resources.cpp:20
config & add_child(const std::string &key)
Definition: config.cpp:743
virtual bool get_leader_ignores_keep() const
Definition: contexts.hpp:776
variant keeps_cache_
Definition: ai.hpp:161
Managing the AIs lifecycle - headers.
virtual const move_map & get_srcdst() const
Definition: contexts.hpp:854
virtual bool get_passive_leader_shares_keep() const
Definition: contexts.hpp:794
iterator end()
end iterator
pathfind::teleport_map get_allowed_teleports(unit_map::iterator &unit_it) const
Definition: ai.cpp:266
virtual attack_result_ptr execute_attack_action(const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon)=0
virtual const move_map & get_enemy_dstsrc() const
Definition: contexts.hpp:709
const_iterator begin() const
Definition: callable.hpp:165
std::vector< formula_input > inputs() const
Definition: callable.hpp:50
virtual const move_map & get_enemy_srcdst() const
Definition: contexts.hpp:721
Encapsulates the map of the game.
Definition: location.hpp:38
bool is_empty() const
Definition: variant.cpp:511
const map_location & dst() const
GLuint res
Definition: glew.h:9258
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition: formula.hpp:31
#define LOG_AI
Definition: ai.cpp:69
void get_locations(std::set< map_location > &locs, bool with_border=false) const
Definition: filter.cpp:495
static config unit_moves(reports::context &rc, const unit *u)
Definition: reports.cpp:582
virtual bool get_support_villages() const
Definition: contexts.hpp:872
Game information for the AI.
variant execute_variant(const variant &var, ai_context &ai_, bool commandline=false)
Definition: ai.cpp:272
virtual bool get_passive_leader() const
Definition: contexts.hpp:788
const std::string & as_string() const
Definition: variant.cpp:603
const_iterator end() const
Definition: callable.hpp:166
bool can_reach_unit(map_location unit_A, map_location unit_B) const
Definition: ai.cpp:920
virtual config to_config() const
Definition: ai.cpp:1030
Default AI contexts.
#define DBG_AI
Definition: ai.cpp:68
formula_ai(readonly_context &context, const config &cfg)
Definition: ai.cpp:105
void set_backup_result(const variant &v)
size_t i
Definition: function.cpp:1057
#define ERR_AI
Definition: ai.cpp:71
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
GLint GLint GLint GLint GLint x
Definition: glew.h:1220
void handle_generic_event(const std::string &)
Definition: ai.cpp:999
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:40
virtual recruit_result_ptr check_recruit_action(const std::string &unit_name, const map_location &where=map_location::null_location(), const map_location &from=map_location::null_location())=0
map_formula_callable & add(const std::string &key, const variant &value)
Definition: formula.cpp:41
boost::shared_ptr< std::vector< unit_const_ptr > > units_
Definition: dialogs.cpp:100
game_logic::map_formula_callable vars_
Definition: ai.hpp:164
game_logic::candidate_action_ptr load_candidate_action_from_config(const config &cfg)
Definition: ai.cpp:80
GLuint const GLchar * name
Definition: glew.h:1782
virtual const team & current_team() const
Definition: contexts.hpp:539
display_chat_manager & get_chat_manager()
std::string to_debug_string(std::vector< const game_logic::formula_callable * > *seen=nullptr, bool verbose=false) const
Definition: variant.cpp:1229
GLclampd n
Definition: glew.h:5903
virtual double get_village_value() const
Definition: contexts.hpp:878
static void add_gamestate_observer(events::observer *event_observer)
Adds observer of game events except ai_user_interact event and ai_sync_network event.
Definition: manager.cpp:371
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
Composite AI contexts.
virtual void get_inputs(std::vector< game_logic::formula_input > *inputs) const
Definition: ai.cpp:863
Standard logging facilities (interface).
recall_list_manager & recall_list()
Definition: team.hpp:220
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
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
#define e
unit_iterator find(size_t id)
Definition: map.cpp:285
void display_message(const std::string &msg) const
Definition: ai.cpp:141
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units)
Definition: teleport.cpp:233
void serialize_from_string(const std::string &str)
Definition: variant.cpp:1136
const map_location & src() const
bool is_string() const
Definition: variant.hpp:79
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
virtual const std::vector< std::string > get_recruitment_pattern() const
Definition: contexts.hpp:836
void add_chat_message(const time_t &time, const std::string &speaker, int side, const std::string &msg, events::chat_handler::MESSAGE_TYPE type, bool bell)
ai_context * ai_ptr_
Definition: ai.hpp:153
virtual const terrain_filter & get_avoid() const
Definition: contexts.hpp:691
std::map< std::string, variant >::const_iterator const_iterator
Definition: callable.hpp:163
This module contains various pathfinding functions and utilities.
void set_ai_context(ai_context *context)
Definition: ai.cpp:159
GLsizei const GLcharARB ** string
Definition: glew.h:4503
unit_map * units
Definition: resources.cpp:35
virtual const move_map & get_dstsrc() const
Definition: contexts.hpp:703
virtual const variant & get_attacks_as_variant() const
Definition: contexts.hpp:685
void init_readonly_context_proxy(readonly_context &target)
Definition: contexts.hpp:521
GLclampf f
Definition: glew.h:3024
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:212
virtual move_result_ptr execute_move_action(const map_location &from, const map_location &to, bool remove_movement=true, bool unreach_is_ok=false)=0
void handle_exception(game_logic::formula_error &e) const
Definition: ai.cpp:122