The Battle for Wesnoth  1.15.1+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, const unit_advancements_aspect& advancements)
175  : action_result(side), attacker_loc_(attacker_loc), defender_loc_(defender_loc), attacker_weapon_(attacker_weapon), aggression_(aggression), advancements_(advancements){
176 }
177 
179 {
180  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
183 
184  if(attacker==resources::gameboard->units().end())
185  {
186  LOG_AI_ACTIONS << "attempt to attack without attacker\n";
188  return;
189  }
190 
191  if (defender==resources::gameboard->units().end())
192  {
193  LOG_AI_ACTIONS << "attempt to attack without defender\n";
195  return;
196  }
197 
198  if(attacker->incapacitated()) {
199  LOG_AI_ACTIONS << "attempt to attack with unit that is petrified\n";
201  return;
202  }
203 
204  if(defender->incapacitated()) {
205  LOG_AI_ACTIONS << "attempt to attack unit that is petrified\n";
207  return;
208  }
209 
210  if(!attacker->attacks_left()) {
211  LOG_AI_ACTIONS << "attempt to attack with no attacks left\n";
213  return;
214  }
215 
216  if(attacker->side()!=get_side()) {
217  LOG_AI_ACTIONS << "attempt to attack with not own unit\n";
219  return;
220  }
221 
222  if(!get_my_team().is_enemy(defender->side())) {
223  LOG_AI_ACTIONS << "attempt to attack unit that is not enemy\n";
225  return;
226  }
227 
228  if (attacker_weapon_!=-1) {
229  if ((attacker_weapon_<0)||(attacker_weapon_ >= static_cast<int>(attacker->attacks().size()))) {
230  LOG_AI_ACTIONS << "invalid weapon selection for the attacker\n";
232  return;
233  }
234  }
235 
237  LOG_AI_ACTIONS << "attacker and defender not adjacent\n";
239  return;
240  }
241 }
242 
244 {
245 }
246 
247 std::string attack_result::do_describe() const
248 {
249  std::stringstream s;
250  s << "attack by side ";
251  s << get_side();
252  s << " from location "<<attacker_loc_;
253  s << " to location "<<defender_loc_;
254  s << " using weapon "<< attacker_weapon_;
255  s << " with aggression "<< aggression_;
256  s <<std::endl;
257  return s.str();
258 }
259 
261 {
262  LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
263  // Stop the user from issuing any commands while the unit is attacking
264  const events::command_disabler disable_commands;
265  //@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.
268 
269  int attacker_weapon = bc.get_attacker_stats().attack_num;
270  int defender_weapon = bc.get_defender_stats().attack_num;
271 
272  if(attacker_weapon < 0) {
274  return;
275  }
276 
279 
282 
283  sim_gamestate_changed(this, gamestate_changed);
284 
285  return;
286  }
287 
288  //FIXME: find a way to 'ask' the ai which advancement should be chosen from synced_commands.cpp .
289  if(!synced_context::is_synced()) //RAII block for set_scontext_synced
290  {
291  wb::real_map rm;
292  //we don't use synced_context::run_in_synced_context because that wouldn't allow us to pass advancements_
293  resources::recorder->add_synced_command("attack", replay_helper::get_attack(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, a_->type_id(),
294  d_->type_id(), a_->level(), d_->level(), resources::tod_manager->turn(),
296  set_scontext_synced sync;
297  attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true, advancements_);
300  sync.do_final_checkup();
301  }
302  else
303  {
304  attack_unit_and_advance(attacker_loc_, defender_loc_, attacker_weapon, defender_weapon, true, advancements_);
305  }
306 
307 
310  //end of ugly hack
311  try {
313  } catch (...) {
314  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
315  throw;
316  }
317 }
318 
320 {
321 }
322 
323 
324 // move_result
326  const map_location& to, bool remove_movement, bool unreach_is_ok)
327  : action_result(side)
328  , from_(from)
329  , to_(to)
330  , remove_movement_(remove_movement)
331  , route_()
332  , unit_location_(from)
333  , unreach_is_ok_(unreach_is_ok)
334  , has_ambusher_(false)
335  , has_interrupted_teleport_(false)
336 {
337 }
338 
340 {
342  if (un==resources::gameboard->units().end()){
344  return nullptr;
345  }
346  const unit *u = &*un;
347  if (u->side() != get_side()) {
349  return nullptr;
350  }
351  if (u->incapacitated()) {
353  return nullptr;
354  }
355  return u;
356 }
357 
359 {
360  if (from_== to_) {
361  if (!remove_movement_ || (un.movement_left() == 0) ) {
363  return false;
364  }
365  return true;
366  }
367 
368  if (un.movement_left() == 0 ) {
370  return false;
371  }
372 
373  if (!to_.valid()) {
375  return false;
376  }
377 
378  team &my_team = get_my_team();
379  const pathfind::shortest_path_calculator calc(un, my_team, resources::gameboard->teams(), resources::gameboard->map());
380 
381  //allowed teleports
382  pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(un, my_team, true);
383 
384  //do an A*-search
385  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)));
386  if (route_->steps.empty()) {
388  return false;
389  }
390  return true;
391 }
392 
394 {
395  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
396  const unit *u = get_unit();
397  if (!u) {
398  return;
399  }
400  if (!test_route(*u)) {
401  return;
402  }
403 }
404 
406 {
407  return unit_location_;
408 }
409 
411 {
412  if (has_ambusher_) {
413  set_error(E_AMBUSHED,false);
414  return;
415  }
418  return;
419  }
420 
421  if (!unreach_is_ok_ && unit_location_!=to_) {
423  return;
424  }
425 }
426 
427 std::string move_result::do_describe() const
428 {
429  std::stringstream s;
430  if (remove_movement_){
431  s << "full move by side ";
432  } else {
433  s << "partial move by side ";
434  }
435  s << get_side();
436  s << " from location "<<from_;
437  s << " to location "<<to_;
438  s <<std::endl;
439  return s.str();
440 }
441 
443 {
444  LOG_AI_ACTIONS << "start of execution of: "<< *this << std::endl;
445  assert(is_success());
446 
448  bool gamestate_changed = false;
449  if(from_ != to_){
450  int step = route_->steps.size();
451  gamestate_changed = simulated_move(get_side(), from_, to_, step, unit_location_);
452  } else {
453  assert(remove_movement_);
454  }
455 
457  if(remove_movement_ && un->movement_left() > 0 && unit_location_ == to_){
458  gamestate_changed = simulated_stopunit(unit_location_, true, false);
459  }
460 
461  sim_gamestate_changed(this, gamestate_changed);
462 
463  return;
464  }
465 
466  ::actions::move_unit_spectator move_spectator(resources::gameboard->units());
467  move_spectator.set_unit(resources::gameboard->units().find(from_));
468 
469  if (from_ != to_) {
470  std::size_t num_steps = ::actions::move_unit_and_record(
471  /*std::vector<map_location> steps*/ route_->steps,
472  /*::actions::undo_list* undo_stack*/ nullptr,
473  /*bool continue_move*/ true,
474  /*bool show_move*/ !preferences::skip_ai_moves(),
475  /*bool* interrupted*/ nullptr,
476  /*::actions::move_unit_spectator* move_spectator*/ &move_spectator);
477 
478  if ( num_steps > 0 ) {
480  } else if ( move_spectator.get_ambusher().valid() ) {
481  // Unlikely, but some types of strange WML (or bad pathfinding)
482  // could cause an ambusher to be found without moving.
484  }
485  } else {
486  assert(remove_movement_);
487  }
488 
489  if (move_spectator.get_unit().valid()){
490  unit_location_ = move_spectator.get_unit()->get_location();
491  if (remove_movement_ && move_spectator.get_unit()->movement_left() > 0 && unit_location_ == to_)
492  {
494  if (!stopunit_res->is_ok()) {
495  set_error(stopunit_res->get_status());
496  }
497  if (stopunit_res->is_gamestate_changed()) {
499  }
500  }
501  } else {
503  }
504 
505  has_ambusher_ = move_spectator.get_ambusher().valid();
507 
508  if (is_gamestate_changed()) {
509  try {
511  } catch (...) {
512  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
513  throw;
514  }
515  }
516 }
517 
519 {
520 }
521 
522 
523 // recall_result
525  const std::string& unit_id, const map_location& where, const map_location& from)
526  : action_result(side)
527  , unit_id_(unit_id)
528  , where_(where)
529  , recall_location_(where)
530  , recall_from_(from)
531  , location_checked_(false)
532 {
533 }
534 
536 {
538  if (!rec) {
540  }
541  return rec;
542 }
543 
545 {
546  if (my_team.gold() < my_team.recall_cost() ) {
548  return false;
549  }
550  return true;
551 }
552 
554 {
555  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
556  const team& my_team = get_my_team();
557  const bool location_specified = recall_location_.valid();
558 
559  //Enough gold?
560  if (!test_enough_gold(my_team)) {
561  return;
562  }
563 
564  //Unit available for recalling?
565  const unit_const_ptr & to_recall = get_recall_unit(my_team);
566  if ( !to_recall ) {
567  return;
568  }
569 
570  // Leader available for recalling?
575  return;
576 
579  return;
580 
583  return;
584 
586  if(location_specified) {
588  return;
589  }
590  FALLTHROUGH; // If the location was not specified, this counts as "OK".
592  location_checked_ = true;
593  }
594 }
595 
597 {
598  if (!resources::gameboard->map().on_board(recall_location_)){
600  return;
601  }
602 
604  if (unit==resources::gameboard->units().end()){
606  return;
607  }
608  if (unit->side() != get_side()){
610  return;
611  }
612 }
613 
614 std::string recall_result::do_describe() const
615 {
616  std::stringstream s;
617  s << "recall by side ";
618  s << get_side();
619  s << " of unit id ["<<unit_id_;
621  s << "] on location "<<where_;
622  } else {
623  s << "] on any suitable location";
624  }
625  s <<std::endl;
626  return s.str();
627 }
628 
630 {
631  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
632  assert(is_success());
633 
634  const events::command_disabler disable_commands;
635 
636  // Assert that recall_location_ has been validated.
637  // This should be implied by is_success() once check_before() has been
638  // called, so this is a guard against future breakage.
639  assert(location_checked_);
640 
642  bool gamestate_changed = simulated_recall(get_side(), unit_id_, recall_location_);
643 
644  sim_gamestate_changed(this, gamestate_changed);
645 
646  return;
647  }
648 
649  // Do the actual recalling.
650  // We ignore possible errors (=unit doesn't exist on the recall list)
651  // because that was the previous behavior.
654  false,
657 
659  try {
661  } catch (...) {
662  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
663  throw;
664  }
665 }
666 
668 {
669 }
670 
671 
672 // recruit_result
674  const std::string& unit_name, const map_location& where, const map_location& from)
675  : action_result(side)
676  , unit_name_(unit_name)
677  , where_(where)
678  , recruit_location_(where)
679  , recruit_from_(from)
680  , location_checked_(false)
681 {
682 }
683 
684 const unit_type *recruit_result::get_unit_type_known(const std::string &recruit)
685 {
686  const unit_type *type = unit_types.find(recruit);
687  if (!type) {
689  return nullptr;
690  }
691  return type;
692 }
693 
695 {
696  if (my_team.gold() < type.cost()) {
698  return false;
699  }
700  return true;
701 }
702 
704 {
705  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
706  const team& my_team = get_my_team();
707  const bool location_specified = recruit_location_.valid();
708 
709  //Unit type known ?
710  const unit_type *s_type = get_unit_type_known(unit_name_);
711  if (!s_type) {
712  return;
713  }
714 
715  //Enough gold?
716  if (!test_enough_gold(my_team, *s_type)) {
717  return;
718  }
719 
720  // Leader available for recruiting?
725  return;
726 
729  return;
730 
733  return;
734 
736  if(location_specified) {
738  return;
739  }
740  FALLTHROUGH; // If the location was not specified, this counts as "OK".
742  location_checked_ = true;
743  }
744 }
745 
747 {
748  if (!resources::gameboard->map().on_board(recruit_location_)) {
750  return;
751  }
752 
754  if (unit==resources::gameboard->units().end()) {
756  return;
757  }
758  if (unit->side() != get_side()) {
760  return;
761  }
762 }
763 
764 std::string recruit_result::do_describe() const
765 {
766  std::stringstream s;
767  s << "recruitment by side ";
768  s << get_side();
769  s << " of unit type ["<<unit_name_;
771  s << "] on location "<<where_;
772  } else {
773  s << "] on any suitable location";
774  }
775  s <<std::endl;
776  return s.str();
777 }
778 
780 {
781  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
782  assert(is_success());
783 
784  const unit_type *u = unit_types.find(unit_name_);
785  const events::command_disabler disable_commands;
786 
787  // Assert that recruit_location_ has been validated.
788  // This should be implied by is_success() once check_before() has been
789  // called, so this is a guard against future breakage.
790  assert(location_checked_ && u != nullptr);
791 
793  bool gamestate_changed = simulated_recruit(get_side(), u, recruit_location_);
794 
795  sim_gamestate_changed(this, gamestate_changed);
796 
797  return;
798  }
799 
801 
803  try {
805  } catch (...) {
806  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
807  throw;
808  }
809 }
810 
812 {
813 }
814 
815 
816 // stopunit_result
817 stopunit_result::stopunit_result( side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
818  : action_result(side), unit_location_(unit_location), remove_movement_(remove_movement), remove_attacks_(remove_attacks)
819 {
820 }
821 
823 {
825  if (un==resources::gameboard->units().end()){
827  return nullptr;
828  }
829  const unit *u = &*un;
830  if (u->side() != get_side()) {
832  return nullptr;
833  }
834  if (u->incapacitated()) {
836  return nullptr;
837  }
838  return u;
839 }
840 
842 {
843  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
844 
845  if (!get_unit()) {
846  return;
847  }
848 }
849 
851 {
853  if (un==resources::gameboard->units().end()){
855  return;
856  }
857  if (remove_movement_ && un->movement_left() != 0) {
859  return;
860  }
861  if (remove_attacks_ && un->attacks_left() != 0) {
863  return;
864  }
865 }
866 
867 std::string stopunit_result::do_describe() const
868 {
869  std::stringstream s;
870  s <<" stopunit by side ";
871  s << get_side();
872  if (remove_movement_){
873  s << " : remove movement ";
874  }
876  s << "and ";
877  }
878  if (remove_attacks_){
879  s << " remove attacks ";
880  }
881  s << "from unit on location "<<unit_location_;
882  s <<std::endl;
883  return s.str();
884 }
885 
887 {
888  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
889  assert(is_success());
891 
894 
895  sim_gamestate_changed(this, gamestate_changed);
896 
897  return;
898  }
899 
900  try {
901  // Don't mark the game state as changed if unit already has no moves or attacks.
902  // Doing so can cause infinite candidate action loops.
903  if (remove_movement_ && un->movement_left() != 0) {
904  un->remove_movement_ai();
907  }
908  if (remove_attacks_ && un->attacks_left() != 0){
909  un->remove_attacks_ai();
911  manager::get_singleton().raise_gamestate_changed();//to be on the safe side
912  }
913  } catch (...) {
914  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
915  throw;
916  }
917 }
918 
920 {
921 }
922 
923 
924 // synced_command_result
925 synced_command_result::synced_command_result( side_number side, const std::string& lua_code, const map_location& location )
926  : action_result(side), lua_code_(lua_code), location_(location)
927 {
928 }
929 
931 {
932  LOG_AI_ACTIONS << " check_before " << *this << std::endl;
933 }
934 
936 {
937 }
938 
940 {
941  std::stringstream s;
942  s <<" synced_command by side ";
943  s << get_side();
944  s <<std::endl;
945  return s.str();
946 }
947 
949 {
951  bool gamestate_changed = simulated_synced_command();
952 
953  sim_gamestate_changed(this, gamestate_changed);
954 
955  return;
956  }
957 
958  LOG_AI_ACTIONS << "start of execution of: " << *this << std::endl;
959  assert(is_success());
960 
961  std::stringstream s;
963  s << "local x1 = " << location_.wml_x() << " local y1 = " << location_.wml_y() << " ";
964  }
965  s << lua_code_;
966 
967  try {
970  } catch (...) {
971  if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked." << std::endl; } //Demotes to DBG "unchecked result" warning
972  throw;
973  }
974 }
975 
977 {
978 }
979 
980 
981 // =======================================================================
982 // STATELESS INTERFACE TO AI ACTIONS
983 // =======================================================================
984 
985 static void execute_or_check(action_result& action, bool execute)
986 {
987  if(execute) {
988  action.execute();
989  } else {
990  action.check_before();
991  }
992 }
993 
995  bool execute,
996  const map_location& attacker_loc,
997  const map_location& defender_loc,
998  int attacker_weapon,
999  double aggression,
1000  const unit_advancements_aspect& advancements)
1001 {
1002  attack_result_ptr action(new attack_result(side, attacker_loc, defender_loc, attacker_weapon, aggression, advancements));
1003  execute_or_check(*action, execute);
1004  return action;
1005 }
1006 
1008  bool execute,
1009  const map_location& from,
1010  const map_location& to,
1011  bool remove_movement,
1012  bool unreach_is_ok)
1013 {
1014  move_result_ptr action(new move_result(side, from, to, remove_movement, unreach_is_ok));
1015  execute_or_check(*action, execute);
1016  return action;
1017 }
1018 
1020  bool execute,
1021  const std::string& unit_id,
1022  const map_location& where,
1023  const map_location& from)
1024 {
1025  recall_result_ptr action(new recall_result(side, unit_id, where, from));
1026  execute_or_check(*action, execute);
1027  return action;
1028 }
1029 
1031  bool execute,
1032  const std::string& unit_name,
1033  const map_location& where,
1034  const map_location& from)
1035 {
1036  recruit_result_ptr action(new recruit_result(side, unit_name, where, from));
1037  execute_or_check(*action, execute);
1038  return action;
1039 }
1040 
1042  bool execute,
1043  const map_location& unit_location,
1044  bool remove_movement,
1045  bool remove_attacks)
1046 {
1047  stopunit_result_ptr action(new stopunit_result(side, unit_location, remove_movement, remove_attacks));
1048  execute_or_check(*action, execute);
1049  return action;
1050 }
1051 
1053  bool execute,
1054  const std::string& lua_code,
1055  const map_location& location)
1056 {
1057  synced_command_result_ptr action(new synced_command_result(side, lua_code, location));
1058  execute_or_check(*action, execute);
1059  return action;
1060 }
1061 
1062 const std::string& actions::get_error_name(int error_code)
1063 {
1064  if (error_names_.empty()){
1065  error_names_.emplace(action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS");
1066  error_names_.emplace(action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED");
1067  error_names_.emplace(action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE");
1068 
1069  error_names_.emplace(attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER");
1070  error_names_.emplace(attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER");
1071  error_names_.emplace(attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER");
1072  error_names_.emplace(attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER");
1073  error_names_.emplace(attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER");
1074  error_names_.emplace(attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER");
1075  error_names_.emplace(attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT");
1076  error_names_.emplace(attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON");
1077  error_names_.emplace(attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON");
1078  error_names_.emplace(attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT");
1079 
1080  error_names_.emplace(move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE");
1081  error_names_.emplace(move_result::E_NO_UNIT, "move_result::E_NO_UNIT");
1082  error_names_.emplace(move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT");
1083  error_names_.emplace(move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT");
1084  error_names_.emplace(move_result::E_AMBUSHED, "move_result::E_AMBUSHED");
1085  error_names_.emplace(move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT");
1086  error_names_.emplace(move_result::E_NOT_REACHED_DESTINATION, "move_result::E_NOT_REACHED_DESTINATION");
1087  error_names_.emplace(move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE");
1088 
1089  error_names_.emplace(recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING");
1090  error_names_.emplace(recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD");
1091  error_names_.emplace(recall_result::E_NO_LEADER," recall_result::E_NO_LEADER");
1092  error_names_.emplace(recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP");
1093  error_names_.emplace(recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION");
1094 
1095  error_names_.emplace(recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING");
1096  error_names_.emplace(recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE");
1097  error_names_.emplace(recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD");
1098  error_names_.emplace(recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER");
1099  error_names_.emplace(recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP");
1100  error_names_.emplace(recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION");
1101 
1102  error_names_.emplace(stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT");
1103  error_names_.emplace(stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT");
1104  error_names_.emplace(stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT");
1105  }
1106  std::map<int,std::string>::iterator i = error_names_.find(error_code);
1107  if (i==error_names_.end()){
1108  ERR_AI_ACTIONS << "error name not available for error #"<<error_code << std::endl;
1109  i = error_names_.find(-1);
1110  assert(i != error_names_.end());
1111  }
1112  return i->second;
1113 }
1114 
1115 std::map<int,std::string> actions::error_names_;
1116 
1117 void sim_gamestate_changed(action_result *result, bool gamestate_changed){
1118  if(gamestate_changed){
1119  result->set_gamestate_changed();
1121  }
1122 }
1123 
1124 } //end of namespace ai
1125 
1126 
1127 std::ostream &operator<<(std::ostream &s, const ai::attack_result& r) {
1128  s << r.do_describe();
1129  return s;
1130 }
1131 
1132 std::ostream &operator<<(std::ostream &s, const ai::move_result& r) {
1133  s << r.do_describe();
1134  return s;
1135 }
1136 
1137 std::ostream &operator<<(std::ostream &s, const ai::recall_result& r) {
1138  s << r.do_describe();
1139  return s;
1140 }
1141 
1142 std::ostream &operator<<(std::ostream &s, const ai::recruit_result& r) {
1143  s << r.do_describe();
1144  return s;
1145 }
1146 
1147 std::ostream &operator<<(std::ostream &s, const ai::stopunit_result& r) {
1148  s << r.do_describe();
1149  return s;
1150 }
1151 
1152 std::ostream &operator<<(std::ostream &s, const ai::synced_command_result& r) {
1153  s << r.do_describe();
1154  return s;
1155 }
bool unreach_is_ok_
Definition: actions.hpp:199
play_controller * controller
Definition: resources.cpp:21
map_location recruit_location_
Definition: actions.hpp:264
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
map_location recruit_from_
Definition: actions.hpp:265
virtual void do_init_for_execution()
Definition: actions.cpp:919
virtual void do_check_after()
Definition: actions.cpp:850
bool simulated_synced_command()
::tod_manager * tod_manager
Definition: resources.cpp:29
bool has_ambusher_
Definition: actions.hpp:200
int h() const
Effective map height.
Definition: map.hpp:128
virtual void do_execute()
Definition: actions.cpp:260
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:1267
game_info & get_info() const
Definition: actions.cpp:162
bool remove_movement_
Definition: actions.hpp:196
map_location recall_from_
Definition: actions.hpp:232
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:535
bool is_success() const
Definition: actions.cpp:152
virtual const unit_map & units() const override
Definition: game_board.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, const unit_advancements_aspect &advancements=unit_advancements_aspect())
Ask the game to attack an enemy defender using our unit attacker from attackers current location...
Definition: actions.cpp:994
virtual void do_check_before()
Definition: actions.cpp:393
This class represents a single unit of a specific type.
Definition: unit.hpp:99
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:446
No leaders exist.
Definition: create.hpp:38
void do_final_checkup(bool dont_throw=false)
const unit * get_unit()
Definition: actions.cpp:339
static manager & get_singleton()
Definition: manager.hpp:150
virtual void do_execute()=0
void raise_gamestate_changed()
Notifies all observers of &#39;ai_gamestate_changed&#39; event.
Definition: manager.cpp:425
map_location unit_location_
Definition: actions.hpp:198
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:976
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:456
virtual void do_check_after()
Definition: actions.cpp:410
bool test_route(const unit &un)
Definition: actions.cpp:358
Managing the AI-Game interaction - AI actions and their results.
map_location recall_location_
Definition: actions.hpp:231
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
virtual void do_init_for_execution()
Definition: actions.cpp:518
const map_location & where_
Definition: actions.hpp:263
const std::string & unit_id_
Definition: actions.hpp:229
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:684
virtual std::string do_describe() const
Definition: actions.cpp:614
std::shared_ptr< move_result > move_result_ptr
Definition: game_info.hpp:84
virtual void do_init_for_execution()
Definition: actions.cpp:811
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:939
virtual void do_check_before()
Definition: actions.cpp:841
unit_type_data unit_types
Definition: types.cpp:1525
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
const unit_advancements_aspect & advancements_
Definition: actions.hpp:162
int get_side() const
Definition: actions.hpp:87
virtual void do_execute()
Definition: actions.cpp:442
std::shared_ptr< recruit_result > recruit_result_ptr
Definition: game_info.hpp:83
const map_location from_
Definition: actions.hpp:194
void check_victory()
Checks to see if a side has won.
virtual const map_location & get_unit_location() const
Definition: actions.cpp:405
static config unit_name(const unit *u)
Definition: reports.cpp:149
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:308
std::shared_ptr< attack_result > attack_result_ptr
Definition: game_info.hpp:81
virtual std::string do_describe() const
Definition: actions.cpp:427
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
void add_synced_command(const std::string &name, const config &command)
Definition: replay.cpp:243
bool has_interrupted_teleport_
Definition: actions.hpp:201
std::shared_ptr< pathfind::plain_route > route_
Definition: actions.hpp:197
std::ostream & operator<<(std::ostream &s, const ai::candidate_action &ca)
Definition: rca.cpp:123
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:463
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:667
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:1019
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:157
static lg::log_domain log_ai_actions("ai/actions")
virtual void do_check_after()
Definition: actions.cpp:746
#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:114
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:596
virtual std::string do_describe() const
Definition: actions.cpp:764
game_board * gameboard
Definition: resources.cpp:20
virtual void do_execute()
Definition: actions.cpp:948
bool location_checked_
Definition: actions.hpp:233
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition: actions.cpp:694
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:925
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:138
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:161
replay * recorder
Definition: resources.cpp:28
virtual void do_check_after()
Definition: actions.cpp:243
virtual void do_execute()
Definition: actions.cpp:629
const bool remove_movement_
Definition: actions.hpp:291
friend void sim_gamestate_changed(action_result *result, bool gamestate_changed)
Definition: actions.cpp:1117
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
attack_result(side_number side, const map_location &attacker_loc, const map_location &defender_loc, int attacker_weapon, double aggression, const unit_advancements_aspect &advancements=unit_advancements_aspect())
Definition: actions.cpp:174
const std::string & unit_name_
Definition: actions.hpp:262
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:703
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:292
virtual void do_check_before()
Definition: actions.cpp:553
unit_iterator find(std::size_t id)
Definition: map.cpp:311
virtual std::string do_describe() const
Definition: actions.cpp:867
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:557
const map_location & defender_loc_
Definition: actions.hpp:159
int w() const
Effective map width.
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
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:544
std::set< map_location > recent_attacks
Definition: game_info.hpp:114
void set_gamestate_changed()
Definition: actions.cpp:142
virtual void do_check_before()
Definition: actions.cpp:930
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:1030
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition: actions.cpp:817
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:1052
bool is_gamestate_changed_
Definition: actions.hpp:125
RECRUIT_CHECK check_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Checks if there is a location on which to place a recruited unit.
Definition: create.cpp:406
bool simulation_
Definition: resources.cpp:35
virtual void do_check_before()
Definition: actions.cpp:178
variant a_
Definition: function.cpp:722
const map_location & unit_location_
Definition: actions.hpp:290
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:1041
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
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:195
static void execute_or_check(action_result &action, bool execute)
Definition: actions.cpp:985
bool is_execution() const
Definition: actions.cpp:157
void maybe_throw_return_to_play_side() const
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:1258
#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:886
virtual std::string do_describe() const =0
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:854
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:303
int side_number
Definition: game_info.hpp:39
virtual std::string do_describe() const
Definition: actions.cpp:247
recall_result(side_number side, const std::string &unit_id, const map_location &where, const map_location &from)
Definition: actions.cpp:524
const map_location where_
Definition: actions.hpp:230
Ensures that the real unit map is active for the duration of the struct&#39;s life.
Definition: manager.hpp:282
const unit * get_unit()
Definition: actions.cpp:822
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:158
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition: actions.cpp:325
static bool is_synced()
virtual void do_check_after()
Definition: actions.cpp:935
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:1183
recruit_result(side_number side, const std::string &unit_name, const map_location &where, const map_location &from)
Definition: actions.cpp:673
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:1007
virtual void do_init_for_execution()
Definition: actions.cpp:319
bool skip_ai_moves()
Definition: game.cpp:762
void attack_unit_and_advance(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display, const ai::unit_advancements_aspect &ai_advancement)
Performs an attack, and advanced the units afterwards.
Definition: attack.cpp:1551
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:1062
const std::vector< map_location > & route_
Definition: move.cpp:292
const map_location & location_
Definition: actions.hpp:309
virtual void do_execute()
Definition: actions.cpp:779
Implement simulated actions.