The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
actions.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2009 - 2025
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"
40
41#include "actions/attack.hpp"
42#include "actions/create.hpp"
43#include "attack_prediction.hpp"
45#include "log.hpp"
46#include "map/map.hpp"
48#include "pathfind/teleport.hpp"
49#include "play_controller.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
59namespace ai {
60
61static 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{
78 DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; //Demotes to DBG "unchecked result" warning
79 }
80}
81
86
91
93{
94 is_execution_ = true;
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
118
123
125{
127 return is_success();
128}
129
130void 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
147
149{
150 return status_;
151}
152
157
159{
160 return is_execution_;
161}
162
167
172
173
174// attack_result
175attack_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
248
249std::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
294 attacker_weapon,
295 defender_weapon,
296 a_->type_id(),
297 d_->type_id(),
298 a_->level(),
299 d_->level(),
302 )
303 );
304
305
308 //end of ugly hack
309 try {
311 } catch (...) {
312 if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
313 throw;
314 }
315}
316
320
321
322// move_result
324 const map_location& to, bool remove_movement, bool unreach_is_ok)
325 : action_result(side)
326 , from_(from)
327 , to_(to)
328 , remove_movement_(remove_movement)
329 , route_()
330 , unit_location_(from)
331 , unreach_is_ok_(unreach_is_ok)
332 , has_ambusher_(false)
333 , has_interrupted_teleport_(false)
334{
335}
336
338{
340 if (un==resources::gameboard->units().end()){
342 return nullptr;
343 }
344 const unit *u = &*un;
345 if (u->side() != get_side()) {
347 return nullptr;
348 }
349 if (u->incapacitated()) {
351 return nullptr;
352 }
353 return u;
354}
355
357{
358 if (from_== to_) {
359 if (!remove_movement_ || (un.movement_left() == 0) ) {
361 return false;
362 }
363 return true;
364 }
365
366 if (un.movement_left() == 0 ) {
368 return false;
369 }
370
371 if (!to_.valid() || !resources::gameboard->map().on_board(to_)) {
373 return false;
374 }
375
376 team &my_team = get_my_team();
377 const pathfind::shortest_path_calculator calc(un, my_team, resources::gameboard->teams(), resources::gameboard->map());
378
379 //allowed teleports
380 pathfind::teleport_map allowed_teleports = pathfind::get_teleport_locations(un, my_team, true);
381
382 //do an A*-search
383 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)));
384 if (route_->steps.empty()) {
386 return false;
387 }
388 return true;
389}
390
392{
393 LOG_AI_ACTIONS << " check_before " << *this;
394 const unit *u = get_unit();
395 if (!u) {
396 return;
397 }
398 if (!test_route(*u)) {
399 return;
400 }
401}
402
404{
405 return unit_location_;
406}
407
409{
410 if (has_ambusher_) {
411 set_error(E_AMBUSHED,false);
412 return;
413 }
416 return;
417 }
418
420 DBG_AI_ACTIONS << "Unit did not reach destination in " << do_describe(); //Demotes to DBG "not reached destination" warning
421 return;
422 }
423}
424
425std::string move_result::do_describe() const
426{
427 std::stringstream s;
428 if (remove_movement_){
429 s << "full move by side ";
430 } else {
431 s << "partial move by side ";
432 }
433 s << get_side();
434 s << " from location "<<from_;
435 s << " to location "<<to_;
436 s <<std::endl;
437 return s.str();
438}
439
441{
442 LOG_AI_ACTIONS << "start of execution of: "<< *this;
443 assert(is_success());
444
446 bool gamestate_changed = false;
447 if(from_ != to_){
448 int step = route_->steps.size();
449 gamestate_changed = simulated_move(get_side(), from_, to_, step, unit_location_);
450 } else {
451 assert(remove_movement_);
452 }
453
455 if(remove_movement_ && un->movement_left() > 0 && unit_location_ == to_){
456 gamestate_changed = simulated_stopunit(unit_location_, true, false);
457 }
458
459 sim_gamestate_changed(this, gamestate_changed);
460
461 return;
462 }
463
465 move_spectator.set_unit(resources::gameboard->units().find(from_));
466 move_spectator.set_ai_move(true);
467
468 if (from_ != to_) {
470 /*std::vector<map_location> steps*/ route_->steps,
471 /*bool continue_move*/ true,
472 /*::actions::move_unit_spectator* move_spectator*/ move_spectator);
473
474 if( move_spectator.get_tiles_entered() > 0) {
476 } else if ( move_spectator.get_ambusher().valid() ) {
477 // Unlikely, but some types of strange WML (or bad pathfinding)
478 // could cause an ambusher to be found without moving.
480 }
481 } else {
482 assert(remove_movement_);
483 }
484
485 if (move_spectator.get_unit().valid()){
486 unit_location_ = move_spectator.get_unit()->get_location();
487 if (remove_movement_ && move_spectator.get_unit()->movement_left() > 0 && unit_location_ == to_)
488 {
490 if (!stopunit_res->is_ok()) {
491 set_error(stopunit_res->get_status());
492 }
493 if (stopunit_res->is_gamestate_changed()) {
495 }
496 }
497 } else {
499 }
500
501 has_ambusher_ = move_spectator.get_ambusher().valid();
503
504 if (is_gamestate_changed()) {
505 try {
507 } catch (...) {
508 if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
509 throw;
510 }
511 }
512}
513
517
518
519// recall_result
521 const std::string& unit_id, const map_location& where, const map_location& from)
522 : action_result(side)
523 , unit_id_(unit_id)
524 , where_(where)
525 , recall_location_(where)
526 , recall_from_(from)
527 , location_checked_(false)
528{
529}
530
532{
534 if (!rec) {
536 }
537 return rec;
538}
539
541{
542 if (my_team.gold() < my_team.recall_cost() ) {
544 return false;
545 }
546 return true;
547}
548
550{
551 LOG_AI_ACTIONS << " check_before " << *this;
552 const team& my_team = get_my_team();
553 const bool location_specified = recall_location_.valid();
554
555 //Enough gold?
556 if (!test_enough_gold(my_team)) {
557 return;
558 }
559
560 //Unit available for recalling?
561 const unit_const_ptr & to_recall = get_recall_unit(my_team);
562 if ( !to_recall ) {
563 return;
564 }
565
566 // Leader available for recalling?
568 case ::actions::RECRUIT_NO_LEADER:
569 case ::actions::RECRUIT_NO_ABLE_LEADER:
571 return;
572
573 case ::actions::RECRUIT_NO_KEEP_LEADER:
575 return;
576
577 case ::actions::RECRUIT_NO_VACANCY:
579 return;
580
581 case ::actions::RECRUIT_ALTERNATE_LOCATION:
582 if(location_specified) {
584 return;
585 }
586 [[fallthrough]]; // If the location was not specified, this counts as "OK".
587 case ::actions::RECRUIT_OK:
588 location_checked_ = true;
589 }
590}
591
593{
594 if (!resources::gameboard->map().on_board(recall_location_)){
596 return;
597 }
598
600 if (unit==resources::gameboard->units().end()){
602 return;
603 }
604 if (unit->side() != get_side()){
606 return;
607 }
608}
609
610std::string recall_result::do_describe() const
611{
612 std::stringstream s;
613 s << "recall by side ";
614 s << get_side();
615 s << " of unit id ["<<unit_id_;
617 s << "] on location "<<where_;
618 } else {
619 s << "] on any suitable location";
620 }
621 s <<std::endl;
622 return s.str();
623}
624
626{
627 LOG_AI_ACTIONS << "start of execution of: " << *this;
628 assert(is_success());
629
630 const events::command_disabler disable_commands;
631
632 // Assert that recall_location_ has been validated.
633 // This should be implied by is_success() once check_before() has been
634 // called, so this is a guard against future breakage.
635 assert(location_checked_);
636
638 bool gamestate_changed = simulated_recall(get_side(), unit_id_, recall_location_);
639
640 sim_gamestate_changed(this, gamestate_changed);
641
642 return;
643 }
644
645 // Do the actual recalling.
649
651 try {
653 } catch (...) {
654 if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
655 throw;
656 }
657}
658
662
663
664// recruit_result
666 const std::string& unit_name, const map_location& where, const map_location& from)
667 : action_result(side)
668 , unit_name_(unit_name)
669 , where_(where)
670 , recruit_location_(where)
671 , recruit_from_(from)
672 , location_checked_(false)
673{
674}
675
676const unit_type *recruit_result::get_unit_type_known(const std::string &recruit)
677{
678 const unit_type *type = unit_types.find(recruit);
679 if (!type) {
681 return nullptr;
682 }
683 return type;
684}
685
686bool recruit_result::test_enough_gold(const team &my_team, const unit_type &type)
687{
688 if (my_team.gold() < type.cost()) {
690 return false;
691 }
692 return true;
693}
694
696{
697 LOG_AI_ACTIONS << " check_before " << *this;
698 const team& my_team = get_my_team();
699 const bool location_specified = recruit_location_.valid();
700
701 //Unit type known ?
703 if (!s_type) {
704 return;
705 }
706
707 //Enough gold?
708 if (!test_enough_gold(my_team, *s_type)) {
709 return;
710 }
711
712 // Leader available for recruiting?
714 case ::actions::RECRUIT_NO_LEADER:
715 case ::actions::RECRUIT_NO_ABLE_LEADER:
717 return;
718
719 case ::actions::RECRUIT_NO_KEEP_LEADER:
721 return;
722
723 case ::actions::RECRUIT_NO_VACANCY:
725 return;
726
727 case ::actions::RECRUIT_ALTERNATE_LOCATION:
728 if(location_specified) {
730 return;
731 }
732 [[fallthrough]]; // If the location was not specified, this counts as "OK".
733 case ::actions::RECRUIT_OK:
734 location_checked_ = true;
735 }
736}
737
739{
740 if (!resources::gameboard->map().on_board(recruit_location_)) {
742 return;
743 }
744
746 if (unit==resources::gameboard->units().end()) {
748 return;
749 }
750 if (unit->side() != get_side()) {
752 return;
753 }
754}
755
757{
758 std::stringstream s;
759 s << "recruitment by side ";
760 s << get_side();
761 s << " of unit type ["<<unit_name_;
763 s << "] on location "<<where_;
764 } else {
765 s << "] on any suitable location";
766 }
767 s <<std::endl;
768 return s.str();
769}
770
772{
773 LOG_AI_ACTIONS << "start of execution of: " << *this;
774 assert(is_success());
775
777 const events::command_disabler disable_commands;
778
779 // Assert that recruit_location_ has been validated.
780 // This should be implied by is_success() once check_before() has been
781 // called, so this is a guard against future breakage.
782 assert(location_checked_ && u != nullptr);
783
785 bool gamestate_changed = simulated_recruit(get_side(), u, recruit_location_);
786
787 sim_gamestate_changed(this, gamestate_changed);
788
789 return;
790 }
791
794
796 try {
798 } catch (...) {
799 if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
800 throw;
801 }
802}
803
807
808
809// stopunit_result
810stopunit_result::stopunit_result( side_number side, const map_location& unit_location, bool remove_movement, bool remove_attacks)
811 : action_result(side), unit_location_(unit_location), remove_movement_(remove_movement), remove_attacks_(remove_attacks)
812{
813}
814
816{
818 if (un==resources::gameboard->units().end()){
820 return nullptr;
821 }
822 const unit *u = &*un;
823 if (u->side() != get_side()) {
825 return nullptr;
826 }
827 if (u->incapacitated()) {
829 return nullptr;
830 }
831 return u;
832}
833
835{
836 LOG_AI_ACTIONS << " check_before " << *this;
837
838 if (!get_unit()) {
839 return;
840 }
841}
842
844{
846 if (un==resources::gameboard->units().end()){
848 return;
849 }
850 if (remove_movement_ && un->movement_left() != 0) {
852 return;
853 }
854 if (remove_attacks_ && un->attacks_left() != 0) {
856 return;
857 }
858}
859
861{
862 std::stringstream s;
863 s <<" stopunit by side ";
864 s << get_side();
865 if (remove_movement_){
866 s << " : remove movement ";
867 }
869 s << "and ";
870 }
871 if (remove_attacks_){
872 s << " remove attacks ";
873 }
874 s << "from unit on location "<<unit_location_;
875 s <<std::endl;
876 return s.str();
877}
878
880{
881 LOG_AI_ACTIONS << "start of execution of: " << *this;
882 assert(is_success());
884
887
888 sim_gamestate_changed(this, gamestate_changed);
889
890 return;
891 }
892
893 try {
894 // Don't mark the game state as changed if unit already has no moves or attacks.
895 // Doing so can cause infinite candidate action loops.
896 if (remove_movement_ && un->movement_left() != 0) {
897 un->remove_movement_ai();
900 }
901 if (remove_attacks_ && un->attacks_left() != 0){
902 un->remove_attacks_ai();
904 manager::get_singleton().raise_gamestate_changed();//to be on the safe side
905 }
906 } catch (...) {
907 if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
908 throw;
909 }
910}
911
915
916
917// synced_command_result
918synced_command_result::synced_command_result( side_number side, const std::string& lua_code, const map_location& location )
919 : action_result(side), lua_code_(lua_code), location_(location)
920{
921}
922
924{
925 LOG_AI_ACTIONS << " check_before " << *this;
926}
927
931
933{
934 std::stringstream s;
935 s <<" synced_command by side ";
936 s << get_side();
937 s <<std::endl;
938 return s.str();
939}
940
942{
944 bool gamestate_changed = simulated_synced_command();
945
946 sim_gamestate_changed(this, gamestate_changed);
947
948 return;
949 }
950
951 LOG_AI_ACTIONS << "start of execution of: " << *this;
952 assert(is_success());
953
954 std::stringstream s;
956 s << "local x1 = " << location_.wml_x() << " local y1 = " << location_.wml_y() << " ";
957 }
958 s << lua_code_;
959
960 try {
963 } catch (...) {
964 if (!is_ok()) { DBG_AI_ACTIONS << "Return value of AI ACTION was not checked."; } //Demotes to DBG "unchecked result" warning
965 throw;
966 }
967}
968
972
973
974// =======================================================================
975// STATELESS INTERFACE TO AI ACTIONS
976// =======================================================================
977
978static void execute_or_check(action_result& action, bool execute)
979{
980 if(execute) {
981 action.execute();
982 } else {
983 action.check_before();
984 }
985}
986
987const std::map<int, std::string> actions::error_names_ {
988 {action_result::AI_ACTION_SUCCESS, "action_result::AI_ACTION_SUCCESS"},
989 {action_result::AI_ACTION_STARTED, "action_result::AI_ACTION_STARTED"},
990 {action_result::AI_ACTION_FAILURE, "action_result::AI_ACTION_FAILURE"},
991
992 {attack_result::E_EMPTY_ATTACKER, "attack_result::E_EMPTY_ATTACKER"},
993 {attack_result::E_EMPTY_DEFENDER, "attack_result::E_EMPTY_DEFENDER"},
994 {attack_result::E_INCAPACITATED_ATTACKER, "attack_result::E_INCAPACITATED_ATTACKER"},
995 {attack_result::E_INCAPACITATED_DEFENDER, "attack_result::E_INCAPACITATED_DEFENDER"},
996 {attack_result::E_NOT_OWN_ATTACKER, "attack_result::E_NOT_OWN_ATTACKER"},
997 {attack_result::E_NOT_ENEMY_DEFENDER, "attack_result::E_NOT_ENEMY_DEFENDER"},
998 {attack_result::E_NO_ATTACKS_LEFT, "attack_result::E_NO_ATTACKS_LEFT"},
999 {attack_result::E_WRONG_ATTACKER_WEAPON, "attack_result::E_WRONG_ATTACKER_WEAPON"},
1000 {attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON, "attack_result::E_UNABLE_TO_CHOOSE_ATTACKER_WEAPON"},
1001 {attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT," attack_result::E_ATTACKER_AND_DEFENDER_NOT_ADJACENT"},
1002
1003 {move_result::E_EMPTY_MOVE, "move_result::E_EMPTY_MOVE"},
1004 {move_result::E_NO_UNIT, "move_result::E_NO_UNIT"},
1005 {move_result::E_NOT_OWN_UNIT, "move_result::E_NOT_OWN_UNIT"},
1006 {move_result::E_INCAPACITATED_UNIT, "move_result::E_INCAPACITATED_UNIT"},
1007 {move_result::E_AMBUSHED, "move_result::E_AMBUSHED"},
1008 {move_result::E_FAILED_TELEPORT, "move_result::E_FAILED_TELEPORT"},
1009 {move_result::E_NO_ROUTE, "move_result::E_NO_ROUTE"},
1010 {move_result::E_OFF_MAP, "move_result::E_OFF_MAP"},
1011
1012 {recall_result::E_NOT_AVAILABLE_FOR_RECALLING, "recall_result::E_NOT_AVAILABLE_FOR_RECALLING"},
1013 {recall_result::E_NO_GOLD, "recall_result::E_NO_GOLD"},
1014 {recall_result::E_NO_LEADER," recall_result::E_NO_LEADER"},
1015 {recall_result::E_LEADER_NOT_ON_KEEP, "recall_result::E_LEADER_NOT_ON_KEEP"},
1016 {recall_result::E_BAD_RECALL_LOCATION, "recall_result::E_BAD_RECALL_LOCATION"},
1017
1018 {recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING, "recruit_result::E_NOT_AVAILABLE_FOR_RECRUITING"},
1019 {recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE, "recruit_result::E_UNKNOWN_OR_DUMMY_UNIT_TYPE"},
1020 {recruit_result::E_NO_GOLD, "recruit_result::E_NO_GOLD"},
1021 {recruit_result::E_NO_LEADER, "recruit_result::E_NO_LEADER"},
1022 {recruit_result::E_LEADER_NOT_ON_KEEP, "recruit_result::E_LEADER_NOT_ON_KEEP"},
1023 {recruit_result::E_BAD_RECRUIT_LOCATION, "recruit_result::E_BAD_RECRUIT_LOCATION"},
1024
1025 {stopunit_result::E_NO_UNIT, "stopunit_result::E_NO_UNIT"},
1026 {stopunit_result::E_NOT_OWN_UNIT, "stopunit_result::E_NOT_OWN_UNIT"},
1027 {stopunit_result::E_INCAPACITATED_UNIT, "stopunit_result::E_INCAPACITATED_UNIT"},
1028};
1029
1031 bool execute,
1032 const map_location& attacker_loc,
1033 const map_location& defender_loc,
1034 int attacker_weapon,
1035 double aggression)
1036{
1037 auto action = std::make_shared<attack_result>(side, attacker_loc, defender_loc, attacker_weapon, aggression);
1038 execute_or_check(*action, execute);
1039 return action;
1040}
1041
1043 bool execute,
1044 const map_location& from,
1045 const map_location& to,
1046 bool remove_movement,
1047 bool unreach_is_ok)
1048{
1049 auto action = std::make_shared<move_result>(side, from, to, remove_movement, unreach_is_ok);
1050 execute_or_check(*action, execute);
1051 return action;
1052}
1053
1055 bool execute,
1056 const std::string& unit_id,
1057 const map_location& where,
1058 const map_location& from)
1059{
1060 auto action = std::make_shared<recall_result>(side, unit_id, where, from);
1061 execute_or_check(*action, execute);
1062 return action;
1063}
1064
1066 bool execute,
1067 const std::string& unit_name,
1068 const map_location& where,
1069 const map_location& from)
1070{
1071 auto action = std::make_shared<recruit_result>(side, unit_name, where, from);
1072 execute_or_check(*action, execute);
1073 return action;
1074}
1075
1077 bool execute,
1078 const map_location& unit_location,
1079 bool remove_movement,
1080 bool remove_attacks)
1081{
1082 auto action = std::make_shared<stopunit_result>(side, unit_location, remove_movement, remove_attacks);
1083 execute_or_check(*action, execute);
1084 return action;
1085}
1086
1088 bool execute,
1089 const std::string& lua_code,
1090 const map_location& location)
1091{
1092 auto action = std::make_shared<synced_command_result>(side, lua_code, location);
1093 execute_or_check(*action, execute);
1094 return action;
1095}
1096
1097const std::string& actions::get_error_name(int error_code)
1098{
1099 auto i = error_names_.find(error_code);
1100 if (i==error_names_.end()){
1101 ERR_AI_ACTIONS << "error name not available for error #"<<error_code;
1102 i = error_names_.find(-1);
1103 assert(i != error_names_.end());
1104 }
1105 return i->second;
1106}
1107
1108void sim_gamestate_changed(action_result *result, bool gamestate_changed){
1109 if(gamestate_changed){
1110 result->set_gamestate_changed();
1112 }
1113}
1114
1115} //end of namespace ai
1116
1117
1118std::ostream &operator<<(std::ostream &s, const ai::attack_result& r) {
1119 s << r.do_describe();
1120 return s;
1121}
1122
1123std::ostream &operator<<(std::ostream &s, const ai::move_result& r) {
1124 s << r.do_describe();
1125 return s;
1126}
1127
1128std::ostream &operator<<(std::ostream &s, const ai::recall_result& r) {
1129 s << r.do_describe();
1130 return s;
1131}
1132
1133std::ostream &operator<<(std::ostream &s, const ai::recruit_result& r) {
1134 s << r.do_describe();
1135 return s;
1136}
1137
1138std::ostream &operator<<(std::ostream &s, const ai::stopunit_result& r) {
1139 s << r.do_describe();
1140 return s;
1141}
1142
1143std::ostream &operator<<(std::ostream &s, const ai::synced_command_result& r) {
1144 s << r.do_describe();
1145 return s;
1146}
static bool is_enemy(std::size_t side, std::size_t other_side)
Various functions that implement attacks and attack calculations.
const std::vector< map_location > & route_
Definition move.cpp:386
#define ERR_AI_ACTIONS
Definition actions.cpp:65
std::ostream & operator<<(std::ostream &s, const ai::attack_result &r)
Definition actions.cpp:1118
#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:72
const unit_map::const_iterator & get_unit() const
get new location of moved unit
Definition move.cpp:96
std::size_t get_tiles_entered() const
Definition move.hpp:100
void set_unit(const unit_map::const_iterator &u)
set the iterator to moved unit
Definition move.cpp:157
void set_ai_move(bool ai_move=true)
Definition move.hpp:93
const unit_map::const_iterator & get_failed_teleport() const
get the location of a failed teleport
Definition move.cpp:78
void clear()
Clears the stack of undoable (and redoable) actions.
Definition undo.cpp:131
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
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:1108
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:1054
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:1087
static const std::string & get_error_name(int error_code)
get human-readable name of the error by code.
Definition actions.cpp:1097
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:1030
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:1042
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:1065
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:1076
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:317
@ 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
static manager & get_singleton()
Definition manager.hpp:140
void raise_gamestate_changed()
Notifies all observers of 'ai_gamestate_changed' event.
Definition manager.cpp:448
game_info & get_active_ai_info_for_side(side_number side)
Gets AI info for active AI of the given side.
Definition manager.cpp:562
virtual const map_location & get_unit_location() const
Definition actions.cpp:403
bool has_interrupted_teleport_
Definition actions.hpp:199
bool test_route(const unit &un)
Definition actions.cpp:356
const map_location to_
Definition actions.hpp:193
const map_location from_
Definition actions.hpp:192
virtual void do_check_after()
Definition actions.cpp:408
virtual void do_execute()
Definition actions.cpp:440
virtual void do_check_before()
Definition actions.cpp:391
std::shared_ptr< pathfind::plain_route > route_
Definition actions.hpp:195
const unit * get_unit()
Definition actions.cpp:337
move_result(side_number side, const map_location &from, const map_location &to, bool remove_movement, bool unreach_is_ok)
Definition actions.cpp:323
bool remove_movement_
Definition actions.hpp:194
virtual void do_init_for_execution()
Definition actions.cpp:514
map_location unit_location_
Definition actions.hpp:196
virtual std::string do_describe() const
Definition actions.cpp:425
recall_result(side_number side, const std::string &unit_id, const map_location &where, const map_location &from)
Definition actions.cpp:520
virtual std::string do_describe() const
Definition actions.cpp:610
virtual void do_init_for_execution()
Definition actions.cpp:659
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:592
bool test_enough_gold(const team &my_team)
Definition actions.cpp:540
@ E_NOT_AVAILABLE_FOR_RECALLING
Definition actions.hpp:208
unit_const_ptr get_recall_unit(const team &my_team)
Definition actions.cpp:531
virtual void do_check_before()
Definition actions.cpp:549
map_location recall_from_
Definition actions.hpp:230
virtual void do_execute()
Definition actions.cpp:625
const map_location where_
Definition actions.hpp:228
virtual void do_check_before()
Definition actions.cpp:695
virtual void do_execute()
Definition actions.cpp:771
map_location recruit_from_
Definition actions.hpp:263
const unit_type * get_unit_type_known(const std::string &recruit)
Definition actions.cpp:676
map_location recruit_location_
Definition actions.hpp:262
virtual std::string do_describe() const
Definition actions.cpp:756
bool test_enough_gold(const team &my_team, const unit_type &type)
Definition actions.cpp:686
const map_location & where_
Definition actions.hpp:261
virtual void do_check_after()
Definition actions.cpp:738
recruit_result(side_number side, const std::string &unit_name, const map_location &where, const map_location &from)
Definition actions.cpp:665
virtual void do_init_for_execution()
Definition actions.cpp:804
const std::string & unit_name_
Definition actions.hpp:260
virtual void do_init_for_execution()
Definition actions.cpp:912
virtual void do_execute()
Definition actions.cpp:879
virtual void do_check_before()
Definition actions.cpp:834
virtual std::string do_describe() const
Definition actions.cpp:860
const bool remove_movement_
Definition actions.hpp:289
const bool remove_attacks_
Definition actions.hpp:290
const unit * get_unit()
Definition actions.cpp:815
stopunit_result(side_number side, const map_location &unit_location, bool remove_movement, bool remove_attacks)
Definition actions.cpp:810
const map_location & unit_location_
Definition actions.hpp:288
virtual void do_check_after()
Definition actions.cpp:843
virtual std::string do_describe() const
Definition actions.cpp:932
virtual void do_check_after()
Definition actions.cpp:928
const map_location & location_
Definition actions.hpp:307
virtual void do_init_for_execution()
Definition actions.cpp:969
synced_command_result(side_number side, const std::string &lua_code, const map_location &location)
Definition actions.cpp:918
virtual void do_check_before()
Definition actions.cpp:923
const std::string & lua_code_
Definition actions.hpp:306
virtual void do_execute()
Definition actions.cpp:941
Computes the statistics of a battle between an attacker and a defender unit.
Definition attack.hpp:183
const battle_context_unit_stats & get_attacker_stats() const
This method returns the statistics of the attacker.
Definition attack.hpp:211
const combatant & get_attacker_combatant(const combatant *prev_def=nullptr)
Get the simulation results.
Definition attack.cpp:361
const battle_context_unit_stats & get_defender_stats() const
This method returns the statistics of the defender.
Definition attack.hpp:217
const combatant & get_defender_combatant(const combatant *prev_def=nullptr)
Definition attack.cpp:368
virtual const gamemap & map() const override
team & get_team(int i)
virtual const unit_map & units() const override
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:347
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 bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
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:198
int gold() const
Definition team.hpp:183
recall_list_manager & recall_list()
Definition team.hpp:242
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.
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:1253
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:145
This class represents a single unit of a specific type.
Definition unit.hpp:39
Various functions related to the creation of units (recruits, recalls, and placed units).
variant a_
Definition function.cpp:821
bool incapacitated() const
Check if the unit has been petrified.
Definition unit.hpp:826
int side() const
The side this unit belongs to.
Definition unit.hpp:249
const map_location & get_location() const
The current map location this unit is at.
Definition unit.hpp:1327
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition unit.hpp:1252
bool tiles_adjacent(const map_location &a, const map_location &b)
Function which tells if two locations are adjacent.
Definition location.cpp:541
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_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, bool continued_move, bool *interrupted)
Wrapper around the other overload.
Definition move.cpp:1435
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:1108
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:978
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:251
::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:163
Implement simulated actions.
int attack_num
Index into unit->attacks() or -1 for none.
Definition attack.hpp:74
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:46
bool valid() const
Definition location.hpp:111
int wml_y() const
Definition location.hpp:187
int wml_x() const
Definition location.hpp:186
static const map_location & null_location()
Definition location.hpp:103
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:1499
Various functions that implement the undoing (and redoing) of in-game commands.
#define i