The Battle for Wesnoth  1.15.5+dev
actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 by Yurii Chernyi <terraninfo@terraninfo.net>
3  Part of the Battle for Wesnoth Project https://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  * Managing the AI-Game interaction - AI actions and their results
17  * @file
18  */
19 
20 /**
21  * A small explanation about what's going on here:
22  * Each action has access to two game_info objects
23  * First is 'info' - real information
24  * Second is 'subjective info' - AIs perception of what's going on
25  * So, when we check_before action, we use 'subjective info' and don't
26  * touch real 'info' at all.
27  * But when we actually want to execute an action, we firstly check
28  * 'subjective info' and then (if subjective check is ok) do the same
29  * check on real 'info'. There's a caveat: if we fail an action based
30  * on real 'info', then we NEED to update AIs knowledge to avoid the ai
31  * doing the same thing again.
32  * So far the use of 'subjective info' is stubbed out.
33  */
34 
35 #include "ai/actions.hpp"
36 #include "ai/manager.hpp"
37 #include "ai/simulated_actions.hpp"
38 
39 #include "actions/attack.hpp"
40 #include "actions/create.hpp"
41 #include "attack_prediction.hpp"
42 #include "preferences/game.hpp"
43 #include "log.hpp"
44 #include "map/map.hpp"
45 #include "mouse_handler_base.hpp"
46 #include "pathfind/teleport.hpp"
47 #include "play_controller.hpp"
49 #include "recall_list_manager.hpp"
50 #include "replay_helper.hpp"
51 #include "resources.hpp"
52 #include "synced_context.hpp"
53 #include "team.hpp"
54 #include "units/unit.hpp"
55 #include "units/ptr.hpp"
56 #include "units/types.hpp"
57 #include "whiteboard/manager.hpp"
58 
59 namespace ai {
60 
61 static lg::log_domain log_ai_actions("ai/actions");
62 #define DBG_AI_ACTIONS LOG_STREAM(debug, log_ai_actions)
63 #define LOG_AI_ACTIONS LOG_STREAM(info, log_ai_actions)
64 #define WRN_AI_ACTIONS LOG_STREAM(warn, log_ai_actions)
65 #define ERR_AI_ACTIONS LOG_STREAM(err, log_ai_actions)
66 
67 // =======================================================================
68 // AI ACTIONS
69 // =======================================================================
71  : return_value_checked_(true),side_(side),status_(AI_ACTION_SUCCESS),is_execution_(false),is_gamestate_changed_(false)
72 {
73 }
74 
76 {
77  if (!return_value_checked_) {
78  DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; //Demotes to DBG "unchecked result" warning
79  }
80 }
81 
83 {
85 }
86 
88 {
90 }
91 
93 {
94  is_execution_ = true;
96  check_before();
97  if (is_success()){
98  try {
99  do_execute();
100  } catch (const return_to_play_side_exception&) {
101  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
102  throw;
103  }
104  }
105  if (is_success()){
106  check_after();
107  }
108  is_execution_ = false;
109 }
110 
112 {
113  return_value_checked_ = false;
114  is_gamestate_changed_ = false;
117 }
118 
120 {
121  return is_gamestate_changed_;
122 }
123 
125 {
126  return_value_checked_ = true;
127  return is_success();
128 }
129 
130 void action_result::set_error(int error_code, bool log_as_error){
131  status_ = error_code;
132  if (is_execution()) {
133  if (log_as_error) {
134  ERR_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
135  } else {
136  LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") in "<< do_describe();
137  }
138  } else {
139  LOG_AI_ACTIONS << "Error #"<<error_code<<" ("<<actions::get_error_name(error_code)<<") when checking "<< do_describe();
140  }
141 }
142 
144 {
145  is_gamestate_changed_ = true;
146 }
147 
149 {
150  return status_;
151 }
152 
154 {
156 }
157 
159 {
160  return is_execution_;
161 }
162 
164 {
166 }
167 
169 {
171 }
172 
173 
174 // attack_result
175 attack_result::attack_result( side_number side, const map_location& attacker_loc, const map_location& defender_loc, int attacker_weapon, double aggression)
176  : action_result(side), attacker_loc_(attacker_loc), defender_loc_(defender_loc), attacker_weapon_(attacker_weapon), aggression_(aggression)
177 {
178 }
179 
181 {
182  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
185 
186  if(attacker==resources::gameboard->units().end())
187  {
188  LOG_AI_ACTIONS << "attempt to attack without attacker\n";
190  return;
191  }
192 
193  if (defender==resources::gameboard->units().end())
194  {
195  LOG_AI_ACTIONS << "attempt to attack without defender\n";
197  return;
198  }
199 
200  if(attacker->incapacitated()) {
201  LOG_AI_ACTIONS << "attempt to attack with unit that is petrified\n";
203  return;
204  }
205 
206  if(defender->incapacitated()) {
207  LOG_AI_ACTIONS << "attempt to attack unit that is petrified\n";
209  return;
210  }
211 
212  if(!attacker->attacks_left()) {
213  LOG_AI_ACTIONS << "attempt to attack with no attacks left\n";
215  return;
216  }
217 
218  if(attacker->side()!=get_side()) {
219  LOG_AI_ACTIONS << "attempt to attack with not own unit\n";
221  return;
222  }
223 
224  if(!get_my_team().is_enemy(defender->side())) {
225  LOG_AI_ACTIONS << "attempt to attack unit that is not enemy\n";
227  return;
228  }
229 
230  if (attacker_weapon_!=-1) {
231  if ((attacker_weapon_<0)||(attacker_weapon_ >= static_cast<int>(attacker->attacks().size()))) {
232  LOG_AI_ACTIONS << "invalid weapon selection for the attacker\n";
234  return;
235  }
236  }
237 
239  LOG_AI_ACTIONS << "attacker and defender not adjacent\n";
241  return;
242  }
243 }
244 
246 {
247 }
248 
250 {
251  std::stringstream s;
252  s << "attack by side ";
253  s << get_side();
254  s << " from location "<<attacker_loc_;
255  s << " to location "<<defender_loc_;
256  s << " using weapon "<< attacker_weapon_;
257  s << " with aggression "<< aggression_;
258  s <<std::endl;
259  return s.str();
260 }
261 
263 {
264  LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
265  // Stop the user from issuing any commands while the unit is attacking
266  const events::command_disabler disable_commands;
267  //@note: yes, this is a decision done here. It's that way because we want to allow a simpler attack 'with whatever weapon is considered best', and because we want to allow the defender to pick it's weapon. That's why aggression is needed. a cleaner solution is needed.
270 
271  int attacker_weapon = bc.get_attacker_stats().attack_num;
272  int defender_weapon = bc.get_defender_stats().attack_num;
273 
274  if(attacker_weapon < 0) {
276  return;
277  }
278 
281 
284 
285  sim_gamestate_changed(this, gamestate_changed);
286 
287  return;
288  }
289 
291  {
296  attacker_weapon,
297  defender_weapon,
298  a_->type_id(),
299  d_->type_id(),
300  a_->level(),
301  d_->level(),
304  )
305  );
306  }
307  else
308  {
309  attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true);
310  }
311 
312 
315  //end of ugly hack
316  try {
318  } catch (...) {
319  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
320  throw;
321  }
322 }
323 
325 {
326 }
327 
328 
329 // move_result
331  const map_location& to, bool remove_movement, bool unreach_is_ok)
332  : action_result(side)
333  , from_(from)
334  , to_(to)
335  , remove_movement_(remove_movement)
336  , route_()
337  , unit_location_(from)
338  , unreach_is_ok_(unreach_is_ok)
339  , has_ambusher_(false)
340  , has_interrupted_teleport_(false)
341 {
342 }
343 
345 {
347  if (un==resources::gameboard->units().end()){
349  return nullptr;
350  }
351  const unit *u = &*un;
352  if (u->side() != get_side()) {
354  return nullptr;
355  }
356  if (u->incapacitated()) {
358  return nullptr;
359  }
360  return u;
361 }
362 
364 {
365  if (from_== to_) {
366  if (!remove_movement_ || (un.movement_left() == 0) ) {
368  return false;
369  }
370  return true;
371  }
372 
373  if (un.movement_left() == 0 ) {
375  return false;
376  }
377 
378  if (!to_.valid()) {
380  return false;
381  }
382 
383  team &my_team = get_my_team();
384  const pathfind::shortest_path_calculator calc(un, my_team, resources::gameboard->teams(), resources::gameboard->map());
385 
386  //allowed teleports
387  pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(un, my_team, true);
388 
389  //do an A*-search
390  route_.reset(new pathfind::plain_route(pathfind::a_star_search(un.get_location(), to_, 10000.0, calc, resources::gameboard->map().w(), resources::gameboard->map().h(), &allowed_teleports)));
391  if (route_->steps.empty()) {
393  return false;
394  }
395  return true;
396 }
397 
399 {
400  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
401  const unit *u = get_unit();
402  if (!u) {
403  return;
404  }
405  if (!test_route(*u)) {
406  return;
407  }
408 }
409 
411 {
412  return unit_location_;
413 }
414 
416 {
417  if (has_ambusher_) {
418  set_error(E_AMBUSHED,false);
419  return;
420  }
423  return;
424  }
425 
426  if (!unreach_is_ok_ && unit_location_!=to_) {
427  DBG_AI_ACTIONS << "Unit did not reach destination in " << do_describe(); //Demotes to DBG "not reached destination" warning
428  return;
429  }
430 }
431 
433 {
434  std::stringstream s;
435  if (remove_movement_){
436  s << "full move by side ";
437  } else {
438  s << "partial move by side ";
439  }
440  s << get_side();
441  s << " from location "<<from_;
442  s << " to location "<<to_;
443  s <<std::endl;
444  return s.str();
445 }
446 
448 {
449  LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
450  assert(is_success());
451 
453  bool gamestate_changed = false;
454  if(from_ != to_){
455  int step = route_->steps.size();
456  gamestate_changed = simulated_move(get_side(), from_, to_, step, unit_location_);
457  } else {
458  assert(remove_movement_);
459  }
460 
462  if(remove_movement_ && un->movement_left() > 0 && unit_location_ == to_){
463  gamestate_changed = simulated_stopunit(unit_location_, true, false);
464  }
465 
466  sim_gamestate_changed(this, gamestate_changed);
467 
468  return;
469  }
470 
471  ::actions::move_unit_spectator move_spectator(resources::gameboard->units());
472  move_spectator.set_unit(resources::gameboard->units().find(from_));
473 
474  if (from_ != to_) {
475  std::size_t num_steps = ::actions::move_unit_and_record(
476  /*std::vector<map_location> steps*/ route_->steps,
477  /*::actions::undo_list* undo_stack*/ nullptr,
478  /*bool continue_move*/ true,
479  /*bool show_move*/ !preferences::skip_ai_moves(),
480  /*bool* interrupted*/ nullptr,
481  /*::actions::move_unit_spectator* move_spectator*/ &move_spectator);
482 
483  if ( num_steps > 0 ) {
485  } else if ( move_spectator.get_ambusher().valid() ) {
486  // Unlikely, but some types of strange WML (or bad pathfinding)
487  // could cause an ambusher to be found without moving.
489  }
490  } else {
491  assert(remove_movement_);
492  }
493 
494  if (move_spectator.get_unit().valid()){
495  unit_location_ = move_spectator.get_unit()->get_location();
496  if (remove_movement_ && move_spectator.get_unit()->movement_left() > 0 && unit_location_ == to_)
497  {
499  if (!stopunit_res->is_ok()) {
500  set_error(stopunit_res->get_status());
501  }
502  if (stopunit_res->is_gamestate_changed()) {
504  }
505  }
506  } else {
508  }
509 
510  has_ambusher_ = move_spectator.get_ambusher().valid();
512 
513  if (is_gamestate_changed()) {
514  try {
516  } catch (...) {
517  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
518  throw;
519  }
520  }
521 }
522 
524 {
525 }
526 
527 
528 // recall_result
530  const std::string& unit_id, const map_location& where, const map_location& from)
531  : action_result(side)
532  , unit_id_(unit_id)
533  , where_(where)
534  , recall_location_(where)
535  , recall_from_(from)
536  , location_checked_(false)
537 {
538 }
539 
541 {
543  if (!rec) {
545  }
546  return rec;
547 }
548 
550 {
551  if (my_team.gold() < my_team.recall_cost() ) {
553  return false;
554  }
555  return true;
556 }
557 
559 {
560  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
561  const team& my_team = get_my_team();
562  const bool location_specified = recall_location_.valid();
563 
564  //Enough gold?
565  if (!test_enough_gold(my_team)) {
566  return;
567  }
568 
569  //Unit available for recalling?
570  const unit_const_ptr & to_recall = get_recall_unit(my_team);
571  if ( !to_recall ) {
572  return;
573  }
574 
575  // Leader available for recalling?
580  return;
581 
584  return;
585 
588  return;
589 
591  if(location_specified) {
593  return;
594  }
595  FALLTHROUGH; // If the location was not specified, this counts as "OK".
597  location_checked_ = true;
598  }
599 }
600 
602 {
603  if (!resources::gameboard->map().on_board(recall_location_)){
605  return;
606  }
607 
609  if (unit==resources::gameboard->units().end()){
611  return;
612  }
613  if (unit->side() != get_side()){
615  return;
616  }
617 }
618 
620 {
621  std::stringstream s;
622  s << "recall by side ";
623  s << get_side();
624  s << " of unit id ["<<unit_id_;
626  s << "] on location "<<where_;
627  } else {
628  s << "] on any suitable location";
629  }
630  s <<std::endl;
631  return s.str();
632 }
633 
635 {
636  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
637  assert(is_success());
638 
639  const events::command_disabler disable_commands;
640 
641  // Assert that recall_location_ has been validated.
642  // This should be implied by is_success() once check_before() has been
643  // called, so this is a guard against future breakage.
644  assert(location_checked_);
645 
647  bool gamestate_changed = simulated_recall(get_side(), unit_id_, recall_location_);
648 
649  sim_gamestate_changed(this, gamestate_changed);
650 
651  return;
652  }
653 
654  // Do the actual recalling.
655  // We ignore possible errors (=unit doesn't exist on the recall list)
656  // because that was the previous behavior.
659  false,
662 
664  try {
666  } catch (...) {
667  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
668  throw;
669  }
670 }
671 
673 {
674 }
675 
676 
677 // recruit_result
679  const std::string& unit_name, const map_location& where, const map_location& from)
680  : action_result(side)
681  , unit_name_(unit_name)
682  , where_(where)
683  , recruit_location_(where)
684  , recruit_from_(from)
685  , location_checked_(false)
686 {
687 }
688 
690 {
691  const unit_type *type = unit_types.find(recruit);
692  if (!type) {
694  return nullptr;
695  }
696  return type;
697 }
698 
700 {
701  if (my_team.gold() < type.cost()) {
703  return false;
704  }
705  return true;
706 }
707 
709 {
710  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
711  const team& my_team = get_my_team();
712  const bool location_specified = recruit_location_.valid();
713 
714  //Unit type known ?
715  const unit_type *s_type = get_unit_type_known(unit_name_);
716  if (!s_type) {
717  return;
718  }
719 
720  //Enough gold?
721  if (!test_enough_gold(my_team, *s_type)) {
722  return;
723  }
724 
725  // Leader available for recruiting?
730  return;
731 
734  return;
735 
738  return;
739 
741  if(location_specified) {
743  return;
744  }
745  FALLTHROUGH; // If the location was not specified, this counts as "OK".
747  location_checked_ = true;
748  }
749 }
750 
752 {
753  if (!resources::gameboard->map().on_board(recruit_location_)) {
755  return;
756  }
757 
759  if (unit==resources::gameboard->units().end()) {
761  return;
762  }
763  if (unit->side() != get_side()) {
765  return;
766  }
767 }
768 
770 {
771  std::stringstream s;
772  s << "recruitment by side ";
773  s << get_side();
774  s << " of unit type ["<<unit_name_;
776  s << "] on location "<<where_;
777  } else {
778  s << "] on any suitable location";
779  }
780  s <<std::endl;
781  return s.str();
782 }
783 
785 {
786  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
787  assert(is_success());
788 
789  const unit_type *u = unit_types.find(unit_name_);
790  const events::command_disabler disable_commands;
791 
792  // Assert that recruit_location_ has been validated.
793  // This should be implied by is_success() once check_before() has been
794  // called, so this is a guard against future breakage.
795  assert(location_checked_ && u != nullptr);
796 
798  bool gamestate_changed = simulated_recruit(get_side(), u, recruit_location_);
799 
800  sim_gamestate_changed(this, gamestate_changed);
801 
802  return;
803  }
804 
806 
808  try {
810  } catch (...) {
811  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
812  throw;
813  }
814 }
815 
817 {
818 }
819 
820 
821 // stopunit_result
822 stopunit_result::stopunit_result( side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
823  : action_result(side), unit_location_(unit_location), remove_movement_(remove_movement), remove_attacks_(remove_attacks)
824 {
825 }
826 
828 {
830  if (un==resources::gameboard->units().end()){
832  return nullptr;
833  }
834  const unit *u = &*un;
835  if (u->side() != get_side()) {
837  return nullptr;
838  }
839  if (u->incapacitated()) {
841  return nullptr;
842  }
843  return u;
844 }
845 
847 {
848  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
849 
850  if (!get_unit()) {
851  return;
852  }
853 }
854 
856 {
858  if (un==resources::gameboard->units().end()){
860  return;
861  }
862  if (remove_movement_ && un->movement_left() != 0) {
864  return;
865  }
866  if (remove_attacks_ && un->attacks_left() != 0) {
868  return;
869  }
870 }
871 
873 {
874  std::stringstream s;
875  s <<" stopunit by side ";
876  s << get_side();
877  if (remove_movement_){
878  s << " : remove movement ";
879  }
881  s << "and ";
882  }
883  if (remove_attacks_){
884  s << " remove attacks ";
885  }
886  s << "from unit on location "<<unit_location_;
887  s <<std::endl;
888  return s.str();
889 }
890 
892 {
893  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
894  assert(is_success());
896 
899 
900  sim_gamestate_changed(this, gamestate_changed);
901 
902  return;
903  }
904 
905  try {
906  // Don't mark the game state as changed if unit already has no moves or attacks.
907  // Doing so can cause infinite candidate action loops.
908  if (remove_movement_ && un->movement_left() != 0) {
909  un->remove_movement_ai();
912  }
913  if (remove_attacks_ && un->attacks_left() != 0){
914  un->remove_attacks_ai();
916  manager::get_singleton().raise_gamestate_changed();//to be on the safe side
917  }
918  } catch (...) {
919  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
920  throw;
921  }
922 }
923 
925 {
926 }
927 
928 
929 // synced_command_result
931  : action_result(side), lua_code_(lua_code), location_(location)
932 {
933 }
934 
936 {
937  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
938 }
939 
941 {
942 }
943 
945 {
946  std::stringstream s;
947  s <<" synced_command by side ";
948  s << get_side();
949  s <<std::endl;
950  return s.str();
951 }
952 
954 {
956  bool gamestate_changed = simulated_synced_command();
957 
958  sim_gamestate_changed(this, gamestate_changed);
959 
960  return;
961  }
962 
963  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
964  assert(is_success());
965 
966  std::stringstream s;
968  s << "local x1 = " << location_.wml_x() << " local y1 = " << location_.wml_y() << " ";
969  }
970  s << lua_code_;
971 
972  try {
975  } catch (...) {
976  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
977  throw;
978  }
979 }
980 
982 {
983 }
984 
985 
986 // =======================================================================
987 // STATELESS INTERFACE TO AI ACTIONS
988 // =======================================================================
989 
990 static void execute_or_check(action_result& action, bool execute)
991 {
992  if(execute) {
993  action.execute();
994  } else {
995  action.check_before();
996  }
997 }
998 
1000  bool execute,
1001  const map_location& attacker_loc,
1002  const map_location& defender_loc,
1003  int attacker_weapon,
1004  double aggression)
1005 {
1006  attack_result_ptr action(new attack_result(side, attacker_loc, defender_loc, attacker_weapon, aggression));
1007  execute_or_check(*action, execute);
1008  return action;
1009 }
1010 
1012  bool execute,
1013  const map_location& from,
1014  const map_location& to,
1015  bool remove_movement,
1016  bool unreach_is_ok)
1017 {
1018  move_result_ptr action(new move_result(side, from, to, remove_movement, unreach_is_ok));
1019  execute_or_check(*action, execute);
1020  return action;
1021 }
1022 
1024  bool execute,
1025  const std::string& unit_id,
1026  const map_location& where,
1027  const map_location& from)
1028 {
1029  recall_result_ptr action(new recall_result(side, unit_id, where, from));
1030  execute_or_check(*action, execute);
1031  return action;
1032 }
1033 
1035  bool execute,
1036  const std::string& unit_name,
1037  const map_location& where,
1038  const map_location& from)
1039 {
1040  recruit_result_ptr action(new recruit_result(side, unit_name, where, from));
1041  execute_or_check(*action, execute);
1042  return action;
1043 }
1044 
1046  bool execute,
1047  const map_location& unit_location,
1048  bool remove_movement,
1049  bool remove_attacks)
1050 {
1051  stopunit_result_ptr action(new stopunit_result(side, unit_location, remove_movement, remove_attacks));
1052  execute_or_check(*action, execute);
1053  return action;
1054 }
1055 
1057  bool execute,
1058  const std::string& lua_code,
1059  const map_location& location)
1060 {
1061  synced_command_result_ptr action(new synced_command_result(side, lua_code, location));
1062  execute_or_check(*action, execute);
1063  return action;
1064 }
1065 
1066 const std::string& actions::get_error_name(int error_code)
1067 {
1068  if (error_names_.empty()){
1069  error_names_.emplace(action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS");
1070  error_names_.emplace(action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED");
1071  error_names_.emplace(action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE");
1072 
1073  error_names_.emplace(attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER");
1074  error_names_.emplace(attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER");
1075  error_names_.emplace(attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER");
1076  error_names_.emplace(attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER");
1077  error_names_.emplace(attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER");
1078  error_names_.emplace(attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER");
1079  error_names_.emplace(attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT");
1080  error_names_.emplace(attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON");
1081  error_names_.emplace(attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON");
1082  error_names_.emplace(attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT");
1083 
1084  error_names_.emplace(move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE");
1085  error_names_.emplace(move_result::E_NO_UNIT, "move_result::E_NO_UNIT");
1086  error_names_.emplace(move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT");
1087  error_names_.emplace(move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT");
1088  error_names_.emplace(move_result::E_AMBUSHED, "move_result::E_AMBUSHED");
1089  error_names_.emplace(move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT");
1090  error_names_.emplace(move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE");
1091 
1092  error_names_.emplace(recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING");
1093  error_names_.emplace(recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD");
1094  error_names_.emplace(recall_result::E_NO_LEADER," recall_result::E_NO_LEADER");
1095  error_names_.emplace(recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP");
1096  error_names_.emplace(recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION");
1097 
1098  error_names_.emplace(recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING");
1099  error_names_.emplace(recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE");
1100  error_names_.emplace(recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD");
1101  error_names_.emplace(recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER");
1102  error_names_.emplace(recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP");
1103  error_names_.emplace(recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION");
1104 
1105  error_names_.emplace(stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT");
1106  error_names_.emplace(stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT");
1107  error_names_.emplace(stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT");
1108  }
1109  std::map<int,std::string>::iterator i = error_names_.find(error_code);
1110  if (i==error_names_.end()){
1111  ERR_AI_ACTIONS << "error name not available for error #"<<error_code << std::endl;
1112  i = error_names_.find(-1);
1113  assert(i != error_names_.end());
1114  }
1115  return i->second;
1116 }
1117 
1118 std::map<int,std::string> actions::error_names_;
1119 
1120 void sim_gamestate_changed(action_result *result, bool gamestate_changed){
1121  if(gamestate_changed){
1122  result->set_gamestate_changed();
1124  }
1125 }
1126 
1127 } //end of namespace ai
1128 
1129 
1130 std::ostream &operator<<(std::ostream &s, const ai::attack_result& r) {
1131  s << r.do_describe();
1132  return s;
1133 }
1134 
1135 std::ostream &operator<<(std::ostream &s, const ai::move_result& r) {
1136  s << r.do_describe();
1137  return s;
1138 }
1139 
1140 std::ostream &operator<<(std::ostream &s, const ai::recall_result& r) {
1141  s << r.do_describe();
1142  return s;
1143 }
1144 
1145 std::ostream &operator<<(std::ostream &s, const ai::recruit_result& r) {
1146  s << r.do_describe();
1147  return s;
1148 }
1149 
1150 std::ostream &operator<<(std::ostream &s, const ai::stopunit_result& r) {
1151  s << r.do_describe();
1152  return s;
1153 }
1154 
1155 std::ostream &operator<<(std::ostream &s, const ai::synced_command_result& r) {
1156  s << r.do_describe();
1157  return s;
1158 }
bool unreach_is_ok_
Definition: actions.hpp:195
map_location recruit_location_
Definition: actions.hpp:260
map_location recruit_from_
Definition: actions.hpp:261
virtual void do_init_for_execution()
Definition: actions.cpp:924
virtual void do_check_after()
Definition: actions.cpp:855
bool simulated_synced_command()
::tod_manager * tod_manager
Definition: resources.cpp:29
bool has_ambusher_
Definition: actions.hpp:196
int h() const
Effective map height, in hexes.
Definition: map.hpp:128
virtual void do_execute()
Definition: actions.cpp:262
void check_before()
Definition: actions.cpp:87
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:1159
game_info & get_info() const
Definition: actions.cpp:163
bool remove_movement_
Definition: actions.hpp:192
map_location recall_from_
Definition: actions.hpp:228
int get_status() const
Definition: actions.cpp:148
std::shared_ptr< stopunit_result > stopunit_result_ptr
Definition: game_info.hpp:86
unit_const_ptr get_recall_unit(const team &my_team)
Definition: actions.cpp:540
bool is_success() const
Definition: actions.cpp:153
virtual const unit_map & units() const override
Definition: game_board.hpp:114
virtual void do_check_before()
Definition: actions.cpp:398
This class represents a single unit of a specific type.
Definition: unit.hpp:129
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:206
static std::map< int, std::string > error_names_
Definition: actions.hpp:441
No leaders exist.
Definition: create.hpp:38
const unit * get_unit()
Definition: actions.cpp:344
static manager & get_singleton()
Definition: manager.hpp:151
virtual void do_execute()=0
void raise_gamestate_changed()
Notifies all observers of &#39;ai_gamestate_changed&#39; event.
Definition: manager.cpp:434
map_location unit_location_
Definition: actions.hpp:194
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition: move.cpp:95
virtual void do_init_for_execution()
Definition: actions.cpp:981
Various functions that implement attacks and attack calculations.
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:460
virtual void do_check_after()
Definition: actions.cpp:415
bool test_route(const unit &un)
Definition: actions.cpp:363
Managing the AI-Game interaction - AI actions and their results.
map_location recall_location_
Definition: actions.hpp:227
game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
Definition: manager.cpp:716
virtual void do_init_for_execution()
Definition: actions.cpp:523
const map_location & where_
Definition: actions.hpp:259
const std::string & unit_id_
Definition: actions.hpp:225
double average_hp(unsigned int healing=0) const
What&#39;s the average hp (weighted average of hp_dist).
const unit_type * get_unit_type_known(const std::string &recruit)
Definition: actions.cpp:689
virtual std::string do_describe() const
Definition: actions.cpp:619
std::shared_ptr< move_result > move_result_ptr
Definition: game_info.hpp:84
virtual void do_init_for_execution()
Definition: actions.cpp:816
virtual const gamemap & map() const override
Definition: game_board.hpp:109
int wml_x() const
Definition: location.hpp:157
virtual std::string do_describe() const
Definition: actions.cpp:944
virtual void do_check_before()
Definition: actions.cpp:846
unit_type_data unit_types
Definition: types.cpp:1378
Recruitment OK, but not at the specified location.
Definition: create.hpp:42
std::size_t move_unit_and_record(const std::vector< map_location > &steps, undo_list *undo_stack, bool continued_move, bool show_move, bool *interrupted, move_unit_spectator *move_spectator)
Moves a unit across the board.
Definition: move.cpp:1246
int get_side() const
Definition: actions.hpp:86
virtual void do_execute()
Definition: actions.cpp:447
std::shared_ptr< recruit_result > recruit_result_ptr
Definition: game_info.hpp:83
const map_location from_
Definition: actions.hpp:190
virtual const map_location & get_unit_location() const
Definition: actions.cpp:410
static config unit_name(const unit *u)
Definition: reports.cpp:159
action_result(side_number side)
Definition: actions.cpp:70
bool simulated_attack(const map_location &attacker_loc, const map_location &defender_loc, double attacker_hp, double defender_hp)
virtual void do_check_after()=0
-file sdl_utils.hpp
const std::string & lua_code_
Definition: actions.hpp:304
std::shared_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:81
virtual std::string do_describe() const
Definition: actions.cpp:432
void init_for_execution()
Definition: actions.cpp:111
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition: move.cpp:77
bool has_interrupted_teleport_
Definition: actions.hpp:197
std::shared_ptr< pathfind::plain_route > route_
Definition: actions.hpp:193
std::ostream & operator<<(std::ostream &s, const ai::candidate_action &ca)
Definition: rca.cpp:145
const unit_map::const_iterator & get_ambusher() const
get the location of an ambusher
Definition: move.cpp:71
A single unit type that the player may recruit.
Definition: types.hpp:44
int gold() const
Definition: team.hpp:189
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:467
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:29
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
team & get_team(int i)
Definition: game_board.hpp:104
virtual void do_init_for_execution()
Definition: actions.cpp:672
static recall_result_ptr execute_recall_action(side_number side, bool execute, const std::string &unit_id, const map_location &where, const map_location &from)
Ask the game to recall a unit for us on specified location.
Definition: actions.cpp:1023
bool simulated_recall(int side, const std::string &unit_id, const map_location &recall_location)
std::shared_ptr< recall_result > recall_result_ptr
Definition: game_info.hpp:82
unit_ptr find_if_matches_id(const std::string &unit_id)
Find a unit by id.
int cost() const
Definition: types.hpp:162
static lg::log_domain log_ai_actions("ai/actions")
virtual void do_check_after()
Definition: actions.cpp:751
attack_result(side_number side, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression)
Definition: actions.cpp:175
#define ERR_AI_ACTIONS
Definition: actions.cpp:65
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:131
bool return_value_checked_
Definition: actions.hpp:113
int wml_y() const
Definition: location.hpp:158
static void ignore_error_function(const std::string &message, bool heavy)
a function to be passed to run_in_synced_context to ignore the error.
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:54
bool valid() const
Definition: location.hpp:93
virtual void do_check_after()
Definition: actions.cpp:601
virtual std::string do_describe() const
Definition: actions.cpp:769
game_board * gameboard
Definition: resources.cpp:20
virtual void do_execute()
Definition: actions.cpp:953
bool location_checked_
Definition: actions.hpp:229
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition: actions.cpp:699
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:173
synced_command_result(side_number side, const std::string &lua_code, const map_location &location)
Definition: actions.cpp:930
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:139
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:53
double aggression_
Definition: actions.hpp:159
virtual void do_check_after()
Definition: actions.cpp:245
virtual void do_execute()
Definition: actions.cpp:634
const bool remove_movement_
Definition: actions.hpp:287
friend void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1120
No vacant castle tiles around a leader on a keep.
Definition: create.hpp:41
virtual void do_check_before()=0
virtual void do_init_for_execution()=0
const std::string & unit_name_
Definition: actions.hpp:258
std::shared_ptr< synced_command_result > synced_command_result_ptr
Definition: game_info.hpp:87
static config get_attack(const map_location &a, const map_location &b, int att_weapon, int def_weapon, const std::string &attacker_type_id, const std::string &defender_type_id, int attacker_lvl, int defender_lvl, const std::size_t turn, const time_of_day &t)
virtual void do_check_before()
Definition: actions.cpp:708
int recall_cost() const
Definition: team.hpp:193
Encapsulates the map of the game.
Definition: location.hpp:42
const bool remove_attacks_
Definition: actions.hpp:288
virtual void do_check_before()
Definition: actions.cpp:558
unit_iterator find(std::size_t id)
Definition: map.cpp:311
virtual std::string do_describe() const
Definition: actions.cpp:872
Various functions related to the creation of units (recruits, recalls, and placed units)...
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:514
const map_location & defender_loc_
Definition: actions.hpp:157
int w() const
Effective map width, in hexes.
Definition: map.hpp:125
void check_after()
Definition: actions.cpp:82
std::size_t i
Definition: function.cpp:933
RECRUIT_CHECK check_recall_location(const int side, map_location &recall_location, map_location &recall_from, const unit &unit_recall)
Checks if there is a location on which to recall unit_recall.
Definition: create.cpp:277
No able leaders are on a keep.
Definition: create.hpp:40
void attack_unit_and_advance(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display)
Performs an attack, and advanced the units afterwards.
Definition: attack.cpp:1565
bool simulated_stopunit(const map_location &unit_location, bool remove_movement, bool remove_attacks)
static map_location::DIRECTION s
bool test_enough_gold(const team &my_team)
Definition: actions.cpp:549
std::set< map_location > recent_attacks
Definition: game_info.hpp:114
static attack_result_ptr execute_attack_action(side_number side, bool execute, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression)
Ask the game to attack an enemy defender using our unit attacker from attackers current location...
Definition: actions.cpp:999
void set_gamestate_changed()
Definition: actions.cpp:143
virtual void do_check_before()
Definition: actions.cpp:935
static recruit_result_ptr execute_recruit_action(side_number side, bool execute, const std::string &unit_name, const map_location &where, const map_location &from)
Ask the game to recruit a unit for us on specified location.
Definition: actions.cpp:1034
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition: actions.cpp:822
static synced_command_result_ptr execute_synced_command_action(side_number side, bool execute, const std::string &lua_code, const map_location &location)
Ask the game to run Lua code.
Definition: actions.cpp:1056
bool is_gamestate_changed_
Definition: actions.hpp:124
RECRUIT_CHECK check_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Checks if there is a location on which to place a recruited unit.
Definition: create.cpp:407
bool simulation_
Definition: resources.cpp:35
virtual void do_check_before()
Definition: actions.cpp:180
variant a_
Definition: function.cpp:722
const map_location & unit_location_
Definition: actions.hpp:286
static stopunit_result_ptr execute_stopunit_action(side_number side, bool execute, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Ask the game to remove unit movements and/or attack.
Definition: actions.cpp:1045
void set_error(int error_code, bool log_as_error=true)
Definition: actions.cpp:130
#define DBG_AI_ACTIONS
Definition: actions.cpp:62
void set_unit(const unit_map::const_iterator &u)
set the iterator to moved unit
Definition: move.cpp:133
static bool run_and_throw(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
team & get_my_team() const
Definition: actions.cpp:168
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
checks whether we are currently running in a synced context, and if not we enters it...
const map_location to_
Definition: actions.hpp:191
static void execute_or_check(action_result &action, bool execute)
Definition: actions.cpp:990
bool is_execution() const
Definition: actions.cpp:158
bool find(E event, F functor)
Tests whether an event handler is available.
bool simulated_move(int side, const map_location &from, const map_location &to, int steps, map_location &unit_location)
bool is_gamestate_changed() const
Definition: actions.cpp:119
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1343
#define LOG_AI_ACTIONS
Definition: actions.cpp:63
bool simulated_recruit(int side, const unit_type *u, const map_location &recruit_location)
Standard logging facilities (interface).
const teleport_map get_teleport_locations(const unit &u, const team &viewing_team, bool see_all, bool ignore_units, bool check_vision)
Definition: teleport.cpp:260
recall_list_manager & recall_list()
Definition: team.hpp:215
static const map_location & null_location()
Definition: location.hpp:85
virtual void do_execute()
Definition: actions.cpp:891
virtual std::string do_describe() const =0
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:904
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
int turn() const
int side() const
The side this unit belongs to.
Definition: unit.hpp:341
int side_number
Definition: game_info.hpp:39
virtual std::string do_describe() const
Definition: actions.cpp:249
recall_result(side_number side, const std::string &unit_id, const map_location &where, const map_location &from)
Definition: actions.cpp:529
const map_location where_
Definition: actions.hpp:226
const unit * get_unit()
Definition: actions.cpp:827
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)
bool valid() const
Definition: map.hpp:276
const map_location & attacker_loc_
Definition: actions.hpp:156
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition: actions.cpp:330
static bool is_synced()
virtual void do_check_after()
Definition: actions.cpp:940
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:200
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1268
recruit_result(side_number side, const std::string &unit_name, const map_location &where, const map_location &from)
Definition: actions.cpp:678
virtual ~action_result()
Definition: actions.cpp:75
static move_result_ptr execute_move_action(side_number side, bool execute, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok=false)
Ask the game to move our unit from location &#39;from&#39; to location &#39;to&#39;, optionally - doing a partial mov...
Definition: actions.cpp:1011
virtual void do_init_for_execution()
Definition: actions.cpp:324
bool skip_ai_moves()
Definition: game.cpp:772
No leaders able to recall/recruit the given unit/type.
Definition: create.hpp:39
static const std::string & get_error_name(int error_code)
get human-readable name of the error by code.
Definition: actions.cpp:1066
const std::vector< map_location > & route_
Definition: move.cpp:292
const map_location & location_
Definition: actions.hpp:305
virtual void do_execute()
Definition: actions.cpp:784
Implement simulated actions.