The Battle for Wesnoth  1.17.23+dev
actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2023
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."; //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."; } //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;
187 
188  if(attacker==resources::gameboard->units().end())
189  {
190  LOG_AI_ACTIONS << "attempt to attack without attacker";
192  return;
193  }
194 
195  if (defender==resources::gameboard->units().end())
196  {
197  LOG_AI_ACTIONS << "attempt to attack without defender";
199  return;
200  }
201 
202  if(attacker->incapacitated()) {
203  LOG_AI_ACTIONS << "attempt to attack with unit that is petrified";
205  return;
206  }
207 
208  if(defender->incapacitated()) {
209  LOG_AI_ACTIONS << "attempt to attack unit that is petrified";
211  return;
212  }
213 
214  if(!attacker->attacks_left()) {
215  LOG_AI_ACTIONS << "attempt to attack with no attacks left";
217  return;
218  }
219 
220  if(attacker->side()!=get_side()) {
221  LOG_AI_ACTIONS << "attempt to attack with not own unit";
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";
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";
236  return;
237  }
238  }
239 
241  LOG_AI_ACTIONS << "attacker and defender not adjacent";
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;
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."; } //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() || !resources::gameboard->map().on_board(to_)) {
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;
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;
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."; } //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;
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;
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."; } //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;
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;
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."; } //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;
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;
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."; } //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;
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;
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."; } //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 
1003 const std::map<int, std::string> actions::error_names_ {
1004  {action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS"},
1005  {action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED"},
1006  {action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE"},
1007 
1008  {attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER"},
1009  {attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER"},
1010  {attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER"},
1011  {attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER"},
1012  {attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER"},
1013  {attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER"},
1014  {attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT"},
1015  {attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON"},
1016  {attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON"},
1017  {attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT"},
1018 
1019  {move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE"},
1020  {move_result::E_NO_UNIT, "move_result::E_NO_UNIT"},
1021  {move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT"},
1022  {move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT"},
1023  {move_result::E_AMBUSHED, "move_result::E_AMBUSHED"},
1024  {move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT"},
1025  {move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE"},
1026  {move_result::E_OFF_MAP, "move_result::E_OFF_MAP"},
1027 
1028  {recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING"},
1029  {recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD"},
1030  {recall_result::E_NO_LEADER," recall_result::E_NO_LEADER"},
1031  {recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP"},
1032  {recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION"},
1033 
1034  {recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING"},
1035  {recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE"},
1036  {recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD"},
1037  {recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER"},
1038  {recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP"},
1039  {recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION"},
1040 
1041  {stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT"},
1042  {stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT"},
1043  {stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT"},
1044 };
1045 
1047  bool execute,
1048  const map_location& attacker_loc,
1049  const map_location& defender_loc,
1050  int attacker_weapon,
1051  double aggression)
1052 {
1053  auto action = std::make_shared<attack_result>(side, attacker_loc, defender_loc, attacker_weapon, aggression);
1054  execute_or_check(*action, execute);
1055  return action;
1056 }
1057 
1059  bool execute,
1060  const map_location& from,
1061  const map_location& to,
1062  bool remove_movement,
1063  bool unreach_is_ok)
1064 {
1065  auto action = std::make_shared<move_result>(side, from, to, remove_movement, unreach_is_ok);
1066  execute_or_check(*action, execute);
1067  return action;
1068 }
1069 
1071  bool execute,
1072  const std::string& unit_id,
1073  const map_location& where,
1074  const map_location& from)
1075 {
1076  auto action = std::make_shared<recall_result>(side, unit_id, where, from);
1077  execute_or_check(*action, execute);
1078  return action;
1079 }
1080 
1082  bool execute,
1083  const std::string& unit_name,
1084  const map_location& where,
1085  const map_location& from)
1086 {
1087  auto action = std::make_shared<recruit_result>(side, unit_name, where, from);
1088  execute_or_check(*action, execute);
1089  return action;
1090 }
1091 
1093  bool execute,
1094  const map_location& unit_location,
1095  bool remove_movement,
1096  bool remove_attacks)
1097 {
1098  auto action = std::make_shared<stopunit_result>(side, unit_location, remove_movement, remove_attacks);
1099  execute_or_check(*action, execute);
1100  return action;
1101 }
1102 
1104  bool execute,
1105  const std::string& lua_code,
1106  const map_location& location)
1107 {
1108  auto action = std::make_shared<synced_command_result>(side, lua_code, location);
1109  execute_or_check(*action, execute);
1110  return action;
1111 }
1112 
1113 const std::string& actions::get_error_name(int error_code)
1114 {
1115  auto i = error_names_.find(error_code);
1116  if (i==error_names_.end()){
1117  ERR_AI_ACTIONS << "error name not available for error #"<<error_code;
1118  i = error_names_.find(-1);
1119  assert(i != error_names_.end());
1120  }
1121  return i->second;
1122 }
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 }
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:1560
Various functions that implement attacks and attack calculations.
const std::vector< map_location > & route_
Definition: move.cpp:295
#define ERR_AI_ACTIONS
Definition: actions.cpp:67
std::ostream & operator<<(std::ostream &s, const ai::attack_result &r)
Definition: actions.cpp:1134
#define LOG_AI_ACTIONS
Definition: actions.cpp:65
#define DBG_AI_ACTIONS
Definition: actions.cpp:64
Managing the AI-Game interaction - AI actions and their results.
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
const unit_map::const_iterator & get_ambusher() const
get the location of an ambusher
Definition: move.cpp:72
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition: move.cpp:96
void set_unit(const unit_map::const_iterator &u)
set the iterator to moved unit
Definition: move.cpp:134
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition: move.cpp:78
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:212
virtual void do_check_before()=0
void init_for_execution()
Definition: actions.cpp:113
virtual ~action_result()
Definition: actions.cpp:77
int get_status() const
Definition: actions.cpp:150
virtual void do_execute()=0
void check_before()
Definition: actions.cpp:89
bool return_value_checked_
Definition: actions.hpp:114
void set_gamestate_changed()
Definition: actions.cpp:145
game_info & get_info() const
Definition: actions.cpp:165
virtual void do_init_for_execution()=0
void check_after()
Definition: actions.cpp:84
action_result(side_number side)
Definition: actions.cpp:72
bool is_gamestate_changed() const
Definition: actions.cpp:121
friend void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1124
int get_side() const
Definition: actions.hpp:87
virtual void do_check_after()=0
bool is_success() const
Definition: actions.cpp:155
bool is_gamestate_changed_
Definition: actions.hpp:125
team & get_my_team() const
Definition: actions.cpp:170
virtual std::string do_describe() const =0
bool is_execution() const
Definition: actions.cpp:160
void set_error(int error_code, bool log_as_error=true)
Definition: actions.cpp:132
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:1070
static const std::map< int, std::string > error_names_
Definition: actions.hpp:428
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:1103
static const std::string & get_error_name(int error_code)
get human-readable name of the error by code.
Definition: actions.cpp:1113
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:1046
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 'from' to location 'to', optionally - doing a partial mov...
Definition: actions.cpp:1058
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:1081
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:1092
double aggression_
Definition: actions.hpp:160
virtual void do_execute()
Definition: actions.cpp:264
attack_result(side_number side, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression)
Definition: actions.cpp:177
virtual std::string do_describe() const
Definition: actions.cpp:251
virtual void do_check_before()
Definition: actions.cpp:182
const map_location & attacker_loc_
Definition: actions.hpp:157
const map_location & defender_loc_
Definition: actions.hpp:158
virtual void do_init_for_execution()
Definition: actions.cpp:326
@ E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON
Definition: actions.hpp:146
@ E_ATTACKER_AND_DEFENDER_NOT_ADJACENT
Definition: actions.hpp:147
virtual void do_check_after()
Definition: actions.cpp:247
std::set< map_location > recent_attacks
Definition: game_info.hpp:115
void raise_gamestate_changed()
Notifies all observers of 'ai_gamestate_changed' event.
Definition: manager.cpp:443
game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
Definition: manager.cpp:707
static manager & get_singleton()
Definition: manager.hpp:145
virtual const map_location & get_unit_location() const
Definition: actions.cpp:412
bool has_interrupted_teleport_
Definition: actions.hpp:199
bool test_route(const unit &un)
Definition: actions.cpp:365
const map_location to_
Definition: actions.hpp:193
const map_location from_
Definition: actions.hpp:192
virtual void do_check_after()
Definition: actions.cpp:417
virtual void do_execute()
Definition: actions.cpp:449
virtual void do_check_before()
Definition: actions.cpp:400
std::shared_ptr< pathfind::plain_route > route_
Definition: actions.hpp:195
const unit * get_unit()
Definition: actions.cpp:346
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition: actions.cpp:332
bool remove_movement_
Definition: actions.hpp:194
virtual void do_init_for_execution()
Definition: actions.cpp:525
bool has_ambusher_
Definition: actions.hpp:198
map_location unit_location_
Definition: actions.hpp:196
virtual std::string do_describe() const
Definition: actions.cpp:434
bool unreach_is_ok_
Definition: actions.hpp:197
recall_result(side_number side, const std::string &unit_id, const map_location &where, const map_location &from)
Definition: actions.cpp:531
virtual std::string do_describe() const
Definition: actions.cpp:621
virtual void do_init_for_execution()
Definition: actions.cpp:675
map_location recall_location_
Definition: actions.hpp:229
const std::string & unit_id_
Definition: actions.hpp:227
virtual void do_check_after()
Definition: actions.cpp:603
bool test_enough_gold(const team &my_team)
Definition: actions.cpp:551
@ E_NOT_AVAILABLE_FOR_RECALLING
Definition: actions.hpp:208
unit_const_ptr get_recall_unit(const team &my_team)
Definition: actions.cpp:542
bool location_checked_
Definition: actions.hpp:231
virtual void do_check_before()
Definition: actions.cpp:560
map_location recall_from_
Definition: actions.hpp:230
virtual void do_execute()
Definition: actions.cpp:636
const map_location where_
Definition: actions.hpp:228
virtual void do_check_before()
Definition: actions.cpp:711
virtual void do_execute()
Definition: actions.cpp:787
map_location recruit_from_
Definition: actions.hpp:263
const unit_type * get_unit_type_known(const std::string &recruit)
Definition: actions.cpp:692
map_location recruit_location_
Definition: actions.hpp:262
virtual std::string do_describe() const
Definition: actions.cpp:772
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition: actions.cpp:702
const map_location & where_
Definition: actions.hpp:261
virtual void do_check_after()
Definition: actions.cpp:754
@ E_NOT_AVAILABLE_FOR_RECRUITING
Definition: actions.hpp:239
@ E_UNKNOWN_OR_DUMMY_UNIT_TYPE
Definition: actions.hpp:240
recruit_result(side_number side, const std::string &unit_name, const map_location &where, const map_location &from)
Definition: actions.cpp:681
virtual void do_init_for_execution()
Definition: actions.cpp:820
const std::string & unit_name_
Definition: actions.hpp:260
virtual void do_init_for_execution()
Definition: actions.cpp:928
virtual void do_execute()
Definition: actions.cpp:895
virtual void do_check_before()
Definition: actions.cpp:850
virtual std::string do_describe() const
Definition: actions.cpp:876
const bool remove_movement_
Definition: actions.hpp:289
const bool remove_attacks_
Definition: actions.hpp:290
const unit * get_unit()
Definition: actions.cpp:831
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition: actions.cpp:826
const map_location & unit_location_
Definition: actions.hpp:288
virtual void do_check_after()
Definition: actions.cpp:859
virtual std::string do_describe() const
Definition: actions.cpp:948
virtual void do_check_after()
Definition: actions.cpp:944
const map_location & location_
Definition: actions.hpp:307
virtual void do_init_for_execution()
Definition: actions.cpp:985
synced_command_result(side_number side, const std::string &lua_code, const map_location &location)
Definition: actions.cpp:934
virtual void do_check_before()
Definition: actions.cpp:939
const std::string & lua_code_
Definition: actions.hpp:306
virtual void do_execute()
Definition: actions.cpp:957
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:168
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:200
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:442
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:194
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:449
team & get_team(int i)
Definition: game_board.hpp:98
virtual const unit_map & units() const override
Definition: game_board.hpp:113
virtual const gamemap & map() const override
Definition: game_board.hpp:103
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:385
unit_ptr find_if_matches_id(const std::string &unit_id)
Find a unit by id.
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)
static config get_recall(const std::string &unit_id, const map_location &loc, const map_location &from)
static config get_recruit(const std::string &type_id, const map_location &loc, const map_location &from)
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
static void ignore_error_function(const std::string &message)
A function to be passed to run_in_synced_context to ignore the error.
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)
static bool is_synced()
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.
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:76
int recall_cost() const
Definition: team.hpp:181
int gold() const
Definition: team.hpp:177
recall_list_manager & recall_list()
Definition: team.hpp:203
int turn() const
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
unit_iterator find(std::size_t id)
Definition: map.cpp:301
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:1246
A single unit type that the player may recruit.
Definition: types.hpp:46
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:144
This class represents a single unit of a specific type.
Definition: unit.hpp:135
Various functions related to the creation of units (recruits, recalls, and placed units).
variant a_
Definition: function.cpp:757
std::size_t i
Definition: function.cpp:968
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:907
int side() const
The side this unit belongs to.
Definition: unit.hpp:345
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1359
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1284
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:503
Standard logging facilities (interface).
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:280
@ RECRUIT_OK
Recruitment OK, but not at the specified location.
Definition: create.hpp:43
@ RECRUIT_NO_VACANCY
No able leaders are on a keep.
Definition: create.hpp:41
@ RECRUIT_NO_ABLE_LEADER
No leaders exist.
Definition: create.hpp:39
@ RECRUIT_ALTERNATE_LOCATION
No vacant castle tiles around a leader on a keep.
Definition: create.hpp:42
@ RECRUIT_NO_KEEP_LEADER
No leaders able to recall/recruit the given unit/type.
Definition: create.hpp:40
@ RECRUIT_NO_LEADER
Definition: create.hpp:38
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:410
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:1230
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
bool simulated_attack(const map_location &attacker_loc, const map_location &defender_loc, double attacker_hp, double defender_hp)
bool simulated_stopunit(const map_location &unit_location, bool remove_movement, bool remove_attacks)
std::shared_ptr< recruit_result > recruit_result_ptr
Definition: game_info.hpp:84
bool simulated_move(int side, const map_location &from, const map_location &to, int steps, map_location &unit_location)
bool simulated_recruit(int side, const unit_type *u, const map_location &recruit_location)
void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1124
bool simulated_synced_command()
static lg::log_domain log_ai_actions("ai/actions")
std::shared_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:82
std::shared_ptr< stopunit_result > stopunit_result_ptr
Definition: game_info.hpp:87
std::shared_ptr< synced_command_result > synced_command_result_ptr
Definition: game_info.hpp:88
int side_number
Definition: game_info.hpp:40
std::shared_ptr< move_result > move_result_ptr
Definition: game_info.hpp:85
std::shared_ptr< recall_result > recall_result_ptr
Definition: game_info.hpp:83
bool simulated_recall(int side, const std::string &unit_id, const map_location &recall_location)
static void execute_or_check(action_result &action, bool execute)
Definition: actions.cpp:994
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)
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
bool skip_ai_moves()
Definition: game.cpp:731
::tod_manager * tod_manager
Definition: resources.cpp:30
game_board * gameboard
Definition: resources.cpp:21
bool simulation_
Definition: resources.cpp:36
actions::undo_list * undo_stack
Definition: resources.cpp:33
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
static config unit_name(const unit *u)
Definition: reports.cpp:161
Implement simulated actions.
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:54
double average_hp(unsigned int healing=0) const
What's the average hp (weighted average of hp_dist).
Encapsulates the map of the game.
Definition: location.hpp:38
bool valid() const
Definition: location.hpp:89
int wml_y() const
Definition: location.hpp:154
static const map_location & null_location()
Definition: location.hpp:81
int wml_x() const
Definition: location.hpp:153
Structure which holds a single route between one location and another.
Definition: pathfind.hpp:133
bool valid() const
Definition: map.hpp:274
static map_location::DIRECTION s
unit_type_data unit_types
Definition: types.cpp:1465
Various functions that implement the undoing (and redoing) of in-game commands.