The Battle for Wesnoth  1.19.0-dev
actions.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
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"
50 #include "recall_list_manager.hpp"
51 #include "replay_helper.hpp"
52 #include "resources.hpp"
53 #include "synced_context.hpp"
54 #include "team.hpp"
55 #include "units/unit.hpp"
56 #include "units/ptr.hpp"
57 #include "units/types.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."; //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."; } //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;
185 
186  if(attacker==resources::gameboard->units().end())
187  {
188  LOG_AI_ACTIONS << "attempt to attack without attacker";
190  return;
191  }
192 
193  if (defender==resources::gameboard->units().end())
194  {
195  LOG_AI_ACTIONS << "attempt to attack without defender";
197  return;
198  }
199 
200  if(attacker->incapacitated()) {
201  LOG_AI_ACTIONS << "attempt to attack with unit that is petrified";
203  return;
204  }
205 
206  if(defender->incapacitated()) {
207  LOG_AI_ACTIONS << "attempt to attack unit that is petrified";
209  return;
210  }
211 
212  if(!attacker->attacks_left()) {
213  LOG_AI_ACTIONS << "attempt to attack with no attacks left";
215  return;
216  }
217 
218  if(attacker->side()!=get_side()) {
219  LOG_AI_ACTIONS << "attempt to attack with not own unit";
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";
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";
234  return;
235  }
236  }
237 
239  LOG_AI_ACTIONS << "attacker and defender not adjacent";
241  return;
242  }
243 }
244 
246 {
247 }
248 
249 std::string attack_result::do_describe() const
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;
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."; } //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() || !resources::gameboard->map().on_board(to_)) {
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;
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 
432 std::string move_result::do_describe() const
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;
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."; } //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;
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 
619 std::string recall_result::do_describe() const
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;
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.
660  false,
663 
665  try {
667  } catch (...) {
668  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
669  throw;
670  }
671 }
672 
674 {
675 }
676 
677 
678 // recruit_result
680  const std::string& unit_name, const map_location& where, const map_location& from)
681  : action_result(side)
682  , unit_name_(unit_name)
683  , where_(where)
684  , recruit_location_(where)
685  , recruit_from_(from)
686  , location_checked_(false)
687 {
688 }
689 
690 const unit_type *recruit_result::get_unit_type_known(const std::string &recruit)
691 {
692  const unit_type *type = unit_types.find(recruit);
693  if (!type) {
695  return nullptr;
696  }
697  return type;
698 }
699 
701 {
702  if (my_team.gold() < type.cost()) {
704  return false;
705  }
706  return true;
707 }
708 
710 {
711  LOG_AI_ACTIONS << " check_before " << *this;
712  const team& my_team = get_my_team();
713  const bool location_specified = recruit_location_.valid();
714 
715  //Unit type known ?
716  const unit_type *s_type = get_unit_type_known(unit_name_);
717  if (!s_type) {
718  return;
719  }
720 
721  //Enough gold?
722  if (!test_enough_gold(my_team, *s_type)) {
723  return;
724  }
725 
726  // Leader available for recruiting?
731  return;
732 
735  return;
736 
739  return;
740 
742  if(location_specified) {
744  return;
745  }
746  [[fallthrough]]; // If the location was not specified, this counts as "OK".
748  location_checked_ = true;
749  }
750 }
751 
753 {
754  if (!resources::gameboard->map().on_board(recruit_location_)) {
756  return;
757  }
758 
760  if (unit==resources::gameboard->units().end()) {
762  return;
763  }
764  if (unit->side() != get_side()) {
766  return;
767  }
768 }
769 
770 std::string recruit_result::do_describe() const
771 {
772  std::stringstream s;
773  s << "recruitment by side ";
774  s << get_side();
775  s << " of unit type ["<<unit_name_;
777  s << "] on location "<<where_;
778  } else {
779  s << "] on any suitable location";
780  }
781  s <<std::endl;
782  return s.str();
783 }
784 
786 {
787  LOG_AI_ACTIONS << "start of execution of: " << *this;
788  assert(is_success());
789 
790  const unit_type *u = unit_types.find(unit_name_);
791  const events::command_disabler disable_commands;
792 
793  // Assert that recruit_location_ has been validated.
794  // This should be implied by is_success() once check_before() has been
795  // called, so this is a guard against future breakage.
796  assert(location_checked_ && u != nullptr);
797 
799  bool gamestate_changed = simulated_recruit(get_side(), u, recruit_location_);
800 
801  sim_gamestate_changed(this, gamestate_changed);
802 
803  return;
804  }
805 
808 
810  try {
812  } catch (...) {
813  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
814  throw;
815  }
816 }
817 
819 {
820 }
821 
822 
823 // stopunit_result
824 stopunit_result::stopunit_result( side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
825  : action_result(side), unit_location_(unit_location), remove_movement_(remove_movement), remove_attacks_(remove_attacks)
826 {
827 }
828 
830 {
832  if (un==resources::gameboard->units().end()){
834  return nullptr;
835  }
836  const unit *u = &*un;
837  if (u->side() != get_side()) {
839  return nullptr;
840  }
841  if (u->incapacitated()) {
843  return nullptr;
844  }
845  return u;
846 }
847 
849 {
850  LOG_AI_ACTIONS << " check_before " << *this;
851 
852  if (!get_unit()) {
853  return;
854  }
855 }
856 
858 {
860  if (un==resources::gameboard->units().end()){
862  return;
863  }
864  if (remove_movement_ && un->movement_left() != 0) {
866  return;
867  }
868  if (remove_attacks_ && un->attacks_left() != 0) {
870  return;
871  }
872 }
873 
874 std::string stopunit_result::do_describe() const
875 {
876  std::stringstream s;
877  s <<" stopunit by side ";
878  s << get_side();
879  if (remove_movement_){
880  s << " : remove movement ";
881  }
883  s << "and ";
884  }
885  if (remove_attacks_){
886  s << " remove attacks ";
887  }
888  s << "from unit on location "<<unit_location_;
889  s <<std::endl;
890  return s.str();
891 }
892 
894 {
895  LOG_AI_ACTIONS << "start of execution of: " << *this;
896  assert(is_success());
898 
901 
902  sim_gamestate_changed(this, gamestate_changed);
903 
904  return;
905  }
906 
907  try {
908  // Don't mark the game state as changed if unit already has no moves or attacks.
909  // Doing so can cause infinite candidate action loops.
910  if (remove_movement_ && un->movement_left() != 0) {
911  un->remove_movement_ai();
914  }
915  if (remove_attacks_ && un->attacks_left() != 0){
916  un->remove_attacks_ai();
918  manager::get_singleton().raise_gamestate_changed();//to be on the safe side
919  }
920  } catch (...) {
921  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
922  throw;
923  }
924 }
925 
927 {
928 }
929 
930 
931 // synced_command_result
932 synced_command_result::synced_command_result( side_number side, const std::string& lua_code, const map_location& location )
933  : action_result(side), lua_code_(lua_code), location_(location)
934 {
935 }
936 
938 {
939  LOG_AI_ACTIONS << " check_before " << *this;
940 }
941 
943 {
944 }
945 
947 {
948  std::stringstream s;
949  s <<" synced_command by side ";
950  s << get_side();
951  s <<std::endl;
952  return s.str();
953 }
954 
956 {
958  bool gamestate_changed = simulated_synced_command();
959 
960  sim_gamestate_changed(this, gamestate_changed);
961 
962  return;
963  }
964 
965  LOG_AI_ACTIONS << "start of execution of: " << *this;
966  assert(is_success());
967 
968  std::stringstream s;
970  s << "local x1 = " << location_.wml_x() << " local y1 = " << location_.wml_y() << " ";
971  }
972  s << lua_code_;
973 
974  try {
977  } catch (...) {
978  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
979  throw;
980  }
981 }
982 
984 {
985 }
986 
987 
988 // =======================================================================
989 // STATELESS INTERFACE TO AI ACTIONS
990 // =======================================================================
991 
992 static void execute_or_check(action_result& action, bool execute)
993 {
994  if(execute) {
995  action.execute();
996  } else {
997  action.check_before();
998  }
999 }
1000 
1001 const std::map<int, std::string> actions::error_names_ {
1002  {action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS"},
1003  {action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED"},
1004  {action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE"},
1005 
1006  {attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER"},
1007  {attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER"},
1008  {attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER"},
1009  {attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER"},
1010  {attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER"},
1011  {attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER"},
1012  {attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT"},
1013  {attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON"},
1014  {attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON"},
1015  {attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT"},
1016 
1017  {move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE"},
1018  {move_result::E_NO_UNIT, "move_result::E_NO_UNIT"},
1019  {move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT"},
1020  {move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT"},
1021  {move_result::E_AMBUSHED, "move_result::E_AMBUSHED"},
1022  {move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT"},
1023  {move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE"},
1024  {move_result::E_OFF_MAP, "move_result::E_OFF_MAP"},
1025 
1026  {recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING"},
1027  {recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD"},
1028  {recall_result::E_NO_LEADER," recall_result::E_NO_LEADER"},
1029  {recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP"},
1030  {recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION"},
1031 
1032  {recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING"},
1033  {recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE"},
1034  {recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD"},
1035  {recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER"},
1036  {recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP"},
1037  {recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION"},
1038 
1039  {stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT"},
1040  {stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT"},
1041  {stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT"},
1042 };
1043 
1045  bool execute,
1046  const map_location& attacker_loc,
1047  const map_location& defender_loc,
1048  int attacker_weapon,
1049  double aggression)
1050 {
1051  auto action = std::make_shared<attack_result>(side, attacker_loc, defender_loc, attacker_weapon, aggression);
1052  execute_or_check(*action, execute);
1053  return action;
1054 }
1055 
1057  bool execute,
1058  const map_location& from,
1059  const map_location& to,
1060  bool remove_movement,
1061  bool unreach_is_ok)
1062 {
1063  auto action = std::make_shared<move_result>(side, from, to, remove_movement, unreach_is_ok);
1064  execute_or_check(*action, execute);
1065  return action;
1066 }
1067 
1069  bool execute,
1070  const std::string& unit_id,
1071  const map_location& where,
1072  const map_location& from)
1073 {
1074  auto action = std::make_shared<recall_result>(side, unit_id, where, from);
1075  execute_or_check(*action, execute);
1076  return action;
1077 }
1078 
1080  bool execute,
1081  const std::string& unit_name,
1082  const map_location& where,
1083  const map_location& from)
1084 {
1085  auto action = std::make_shared<recruit_result>(side, unit_name, where, from);
1086  execute_or_check(*action, execute);
1087  return action;
1088 }
1089 
1091  bool execute,
1092  const map_location& unit_location,
1093  bool remove_movement,
1094  bool remove_attacks)
1095 {
1096  auto action = std::make_shared<stopunit_result>(side, unit_location, remove_movement, remove_attacks);
1097  execute_or_check(*action, execute);
1098  return action;
1099 }
1100 
1102  bool execute,
1103  const std::string& lua_code,
1104  const map_location& location)
1105 {
1106  auto action = std::make_shared<synced_command_result>(side, lua_code, location);
1107  execute_or_check(*action, execute);
1108  return action;
1109 }
1110 
1111 const std::string& actions::get_error_name(int error_code)
1112 {
1113  auto i = error_names_.find(error_code);
1114  if (i==error_names_.end()){
1115  ERR_AI_ACTIONS << "error name not available for error #"<<error_code;
1116  i = error_names_.find(-1);
1117  assert(i != error_names_.end());
1118  }
1119  return i->second;
1120 }
1121 
1122 void sim_gamestate_changed(action_result *result, bool gamestate_changed){
1123  if(gamestate_changed){
1124  result->set_gamestate_changed();
1126  }
1127 }
1128 
1129 } //end of namespace ai
1130 
1131 
1132 std::ostream &operator<<(std::ostream &s, const ai::attack_result& r) {
1133  s << r.do_describe();
1134  return s;
1135 }
1136 
1137 std::ostream &operator<<(std::ostream &s, const ai::move_result& r) {
1138  s << r.do_describe();
1139  return s;
1140 }
1141 
1142 std::ostream &operator<<(std::ostream &s, const ai::recall_result& r) {
1143  s << r.do_describe();
1144  return s;
1145 }
1146 
1147 std::ostream &operator<<(std::ostream &s, const ai::recruit_result& r) {
1148  s << r.do_describe();
1149  return s;
1150 }
1151 
1152 std::ostream &operator<<(std::ostream &s, const ai::stopunit_result& r) {
1153  s << r.do_describe();
1154  return s;
1155 }
1156 
1157 std::ostream &operator<<(std::ostream &s, const ai::synced_command_result& r) {
1158  s << r.do_describe();
1159  return s;
1160 }
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:1556
Various functions that implement attacks and attack calculations.
const std::vector< map_location > & route_
Definition: move.cpp:294
#define ERR_AI_ACTIONS
Definition: actions.cpp:65
std::ostream & operator<<(std::ostream &s, const ai::attack_result &r)
Definition: actions.cpp:1132
#define LOG_AI_ACTIONS
Definition: actions.cpp:63
#define DBG_AI_ACTIONS
Definition: actions.cpp:62
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:71
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition: move.cpp:95
void set_unit(const unit_map::const_iterator &u)
set the iterator to moved unit
Definition: move.cpp:133
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition: move.cpp:77
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:201
virtual void do_check_before()=0
void init_for_execution()
Definition: actions.cpp:111
virtual ~action_result()
Definition: actions.cpp:75
int get_status() const
Definition: actions.cpp:148
virtual void do_execute()=0
void check_before()
Definition: actions.cpp:87
bool return_value_checked_
Definition: actions.hpp:114
void set_gamestate_changed()
Definition: actions.cpp:143
game_info & get_info() const
Definition: actions.cpp:163
virtual void do_init_for_execution()=0
void check_after()
Definition: actions.cpp:82
action_result(side_number side)
Definition: actions.cpp:70
bool is_gamestate_changed() const
Definition: actions.cpp:119
friend void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1122
int get_side() const
Definition: actions.hpp:87
virtual void do_check_after()=0
bool is_success() const
Definition: actions.cpp:153
bool is_gamestate_changed_
Definition: actions.hpp:125
team & get_my_team() const
Definition: actions.cpp:168
virtual std::string do_describe() const =0
bool is_execution() const
Definition: actions.cpp:158
void set_error(int error_code, bool log_as_error=true)
Definition: actions.cpp:130
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:1068
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:1101
static const std::string & get_error_name(int error_code)
get human-readable name of the error by code.
Definition: actions.cpp:1111
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:1044
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:1056
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:1079
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:1090
double aggression_
Definition: actions.hpp:160
virtual void do_execute()
Definition: actions.cpp:262
attack_result(side_number side, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression)
Definition: actions.cpp:175
virtual std::string do_describe() const
Definition: actions.cpp:249
virtual void do_check_before()
Definition: actions.cpp:180
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:324
@ 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:245
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:441
game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
Definition: manager.cpp:705
static manager & get_singleton()
Definition: manager.hpp:142
virtual const map_location & get_unit_location() const
Definition: actions.cpp:410
bool has_interrupted_teleport_
Definition: actions.hpp:199
bool test_route(const unit &un)
Definition: actions.cpp:363
const map_location to_
Definition: actions.hpp:193
const map_location from_
Definition: actions.hpp:192
virtual void do_check_after()
Definition: actions.cpp:415
virtual void do_execute()
Definition: actions.cpp:447
virtual void do_check_before()
Definition: actions.cpp:398
std::shared_ptr< pathfind::plain_route > route_
Definition: actions.hpp:195
const unit * get_unit()
Definition: actions.cpp:344
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition: actions.cpp:330
bool remove_movement_
Definition: actions.hpp:194
virtual void do_init_for_execution()
Definition: actions.cpp:523
bool has_ambusher_
Definition: actions.hpp:198
map_location unit_location_
Definition: actions.hpp:196
virtual std::string do_describe() const
Definition: actions.cpp:432
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:529
virtual std::string do_describe() const
Definition: actions.cpp:619
virtual void do_init_for_execution()
Definition: actions.cpp:673
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:601
bool test_enough_gold(const team &my_team)
Definition: actions.cpp:549
@ E_NOT_AVAILABLE_FOR_RECALLING
Definition: actions.hpp:208
unit_const_ptr get_recall_unit(const team &my_team)
Definition: actions.cpp:540
bool location_checked_
Definition: actions.hpp:231
virtual void do_check_before()
Definition: actions.cpp:558
map_location recall_from_
Definition: actions.hpp:230
virtual void do_execute()
Definition: actions.cpp:634
const map_location where_
Definition: actions.hpp:228
virtual void do_check_before()
Definition: actions.cpp:709
virtual void do_execute()
Definition: actions.cpp:785
map_location recruit_from_
Definition: actions.hpp:263
const unit_type * get_unit_type_known(const std::string &recruit)
Definition: actions.cpp:690
map_location recruit_location_
Definition: actions.hpp:262
virtual std::string do_describe() const
Definition: actions.cpp:770
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition: actions.cpp:700
const map_location & where_
Definition: actions.hpp:261
virtual void do_check_after()
Definition: actions.cpp:752
@ 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:679
virtual void do_init_for_execution()
Definition: actions.cpp:818
const std::string & unit_name_
Definition: actions.hpp:260
virtual void do_init_for_execution()
Definition: actions.cpp:926
virtual void do_execute()
Definition: actions.cpp:893
virtual void do_check_before()
Definition: actions.cpp:848
virtual std::string do_describe() const
Definition: actions.cpp:874
const bool remove_movement_
Definition: actions.hpp:289
const bool remove_attacks_
Definition: actions.hpp:290
const unit * get_unit()
Definition: actions.cpp:829
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition: actions.cpp:824
const map_location & unit_location_
Definition: actions.hpp:288
virtual void do_check_after()
Definition: actions.cpp:857
virtual std::string do_describe() const
Definition: actions.cpp:946
virtual void do_check_after()
Definition: actions.cpp:942
const map_location & location_
Definition: actions.hpp:307
virtual void do_init_for_execution()
Definition: actions.cpp:983
synced_command_result(side_number side, const std::string &lua_code, const map_location &location)
Definition: actions.cpp:932
virtual void do_check_before()
Definition: actions.cpp:937
const std::string & lua_code_
Definition: actions.hpp:306
virtual void do_execute()
Definition: actions.cpp:955
Computes the statistics of a battle between an attacker and a defender unit.
Definition: attack.hpp:167
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition: attack.hpp:199
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition: attack.cpp:436
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition: attack.hpp:193
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition: attack.cpp:443
team & get_team(int i)
Definition: game_board.hpp:91
virtual const unit_map & units() const override
Definition: game_board.hpp:106
virtual const gamemap & map() const override
Definition: game_board.hpp:96
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition: map.cpp:384
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:74
int recall_cost() const
Definition: team.hpp:179
int gold() const
Definition: team.hpp:175
recall_list_manager & recall_list()
Definition: team.hpp:201
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:302
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:1266
A single unit type that the player may recruit.
Definition: types.hpp:43
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:141
This class represents a single unit of a specific type.
Definition: unit.hpp:133
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:905
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1357
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1282
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition: location.cpp:502
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:278
@ 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:408
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:1229
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
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:1122
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:992
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:728
::tod_manager * tod_manager
Definition: resources.cpp:29
game_board * gameboard
Definition: resources.cpp:20
bool simulation_
Definition: resources.cpp:35
actions::undo_list * undo_stack
Definition: resources.cpp:32
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
static config unit_name(const unit *u)
Definition: reports.cpp:157
Implement simulated actions.
int attack_num
Index into unit->attacks() or -1 for none.
Definition: attack.hpp:53
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:273
static map_location::DIRECTION s
unit_type_data unit_types
Definition: types.cpp:1485
Various functions that implement the undoing (and redoing) of in-game commands.