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