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