The Battle for Wesnoth  1.17.0-dev
stage_rca.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2021
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  * Candidate actions evaluator
18  * @file
19  */
20 
21 #include "ai/default/stage_rca.hpp"
22 
23 #include "ai/manager.hpp"
24 #include "ai/composite/ai.hpp"
25 #include "ai/composite/engine.hpp"
27 #include "ai/composite/rca.hpp"
29 #include "log.hpp"
30 
31 #include <functional>
32 
33 namespace ai {
34 
35 namespace ai_default_rca {
36 
37 static lg::log_domain log_ai_testing_rca_default("ai/stage/rca");
38 #define DBG_AI_TESTING_RCA_DEFAULT LOG_STREAM(debug, log_ai_testing_rca_default)
39 #define LOG_AI_TESTING_RCA_DEFAULT LOG_STREAM(info, log_ai_testing_rca_default)
40 #define ERR_AI_TESTING_RCA_DEFAULT LOG_STREAM(err, log_ai_testing_rca_default)
41 
43  : stage(context,cfg)
44  , candidate_actions_()
45  , cfg_(cfg)
46 {
47 }
48 
50 {
51  //init the candidate actions
52  for (const config &cfg_element : cfg_.child_range("candidate_action")) {
53  engine::parse_candidate_action_from_config(*this,cfg_element,back_inserter(candidate_actions_));
54  }
55 
56  std::function<void(std::vector<candidate_action_ptr>&, const config&)> factory_candidate_actions = [this](std::vector<candidate_action_ptr> &candidate_actions, const config &cfg)
57  {
58  engine::parse_candidate_action_from_config(*this, cfg, std::back_inserter(candidate_actions));
59  };
60  register_vector_property(property_handlers(),"candidate_action",candidate_actions_, factory_candidate_actions);
61 
62 }
63 
65 {
66  config cfg = stage::to_config();
68  cfg.add_child("candidate_action",ca->to_config());
69  }
70  return cfg;
71 }
72 
73 
75 public:
77  {
78  return a->get_max_score() > b->get_max_score();
79  }
80 };
81 
83 {
84  LOG_AI_TESTING_RCA_DEFAULT << "Starting candidate action evaluation loop for side "<< get_side() << std::endl;
85 
87  ca->enable();
88  }
89 
90  //sort candidate actions by max_score DESC
91  std::sort(candidate_actions_.begin(),candidate_actions_.end(),desc_sorter_of_candidate_actions());
92 
93  bool executed = false;
94  bool gamestate_changed = false;
95  do {
96  executed = false;
97  double best_score = candidate_action::BAD_SCORE;
98  candidate_action_ptr best_ptr;
99 
100  //Evaluation
101  for (candidate_action_ptr ca_ptr : candidate_actions_) {
102  if (!ca_ptr->is_enabled()){
103  DBG_AI_TESTING_RCA_DEFAULT << "Skipping disabled candidate action: "<< *ca_ptr << std::endl;
104  continue;
105  }
106 
107  if (ca_ptr->get_max_score()<=best_score) {
108  DBG_AI_TESTING_RCA_DEFAULT << "Ending candidate action evaluation loop because current score "<<best_score<<" is greater than the upper bound of score for remaining candidate actions "<< ca_ptr->get_max_score()<< std::endl;
109  break;
110  }
111 
112  DBG_AI_TESTING_RCA_DEFAULT << "Evaluating candidate action: "<< *ca_ptr << std::endl;
113  double score = ca_ptr->evaluate();
114  DBG_AI_TESTING_RCA_DEFAULT << "Evaluated candidate action to score "<< score << " : " << *ca_ptr << std::endl;
115 
116  if (score>best_score) {
117  best_score = score;
118  best_ptr = ca_ptr;
119  }
120  }
121 
122  //Execution
123  if (best_score>candidate_action::BAD_SCORE) {
124  DBG_AI_TESTING_RCA_DEFAULT << "Executing best candidate action: "<< *best_ptr << std::endl;
125  gamestate_observer gs_o;
126  best_ptr->execute();
127  executed = true;
128  if (!gs_o.is_gamestate_changed()) {
129  //this means that this CA has lied to us in evaluate()
130  //we punish it by disabling it
131  DBG_AI_TESTING_RCA_DEFAULT << "Disabling candidate action because it failed to change the game state: "<< *best_ptr << std::endl;
132  best_ptr->disable();
133  //since we don't re-enable at this play_stage, if we disable this CA, other may get the chance to go.
134  } else {
135  gamestate_changed = true;
136  }
137  } else {
138  LOG_AI_TESTING_RCA_DEFAULT << "Ending candidate action evaluation loop due to best score "<< best_score<<"<="<< candidate_action::BAD_SCORE<<std::endl;
139  }
140  } while (executed);
141  LOG_AI_TESTING_RCA_DEFAULT << "Ended candidate action evaluation loop for side "<< get_side() << std::endl;
143  return gamestate_changed;
144 }
145 
147 {
148  std::vector<std::size_t> tbr; // indexes of elements to be removed
149 
150  for (std::size_t i = 0; i != candidate_actions_.size(); ++i)
151  {
152  if (candidate_actions_[i]->to_be_removed())
153  {
154  tbr.push_back(i); // so we fill the array with the indexes
155  }
156  }
157 
158  for (std::size_t i = 0; i != tbr.size(); ++i)
159  {
160  // we should go downwards, so that index shifts don't affect us
161  std::size_t index = tbr.size() - i - 1; // downcounting for is not possible using unsigned counters, so we hack around
162  std::string path = "stage[" + this->get_id() + "].candidate_action[" + candidate_actions_[tbr[index]]->get_name() + "]";
163 
164  config cfg = config();
165  cfg["path"] = path;
166  cfg["action"] = "delete";
167 
168  ai::manager::get_singleton().modify_active_ai_for_side(this->get_side(), cfg); // we remove the CA
169  }
170 
171 
172 // @note: this code might be more convenient, but is obviously faulty and incomplete, because of iterator invalidation rules
173 // If you see a way to complete it, please contact me(Nephro).
174 // for (std::vector<candidate_action_ptr>::iterator it = candidate_actions_.begin(); it != candidate_actions_.end(); )
175 // {
176 // if ((*it)->to_be_removed())
177 // {
178 // // code to remove a CA
179 // std::string path = "stage[" + this->get_id() + "].candidate_action[" + (*it)->get_name() + "]";
180 //
181 // config cfg = config();
182 // cfg["path"] = path;
183 // cfg["action"] = "delete";
184 //
185 // ai::manager::get_singleton().modify_active_ai_for_side(this->get_side(), cfg);
186 // }
187 // else
188 // {
189 // ++it; // @note: should I modify this to a while loop?
190 // }
191 // }
192 }
193 
195 {
196  return *this;
197 }
198 
200 {
201 }
202 
203 } // end of namespace testing_ai_default
204 
205 } // end of namespace ai
std::shared_ptr< candidate_action > candidate_action_ptr
Definition: rca.hpp:145
static lg::log_domain log_ai_testing_rca_default("ai/stage/rca")
bool is_gamestate_changed()
Check if the gamestate has changed since last reset reset is done once on construction, and can be done by hand via reset() method.
static manager & get_singleton()
Definition: manager.hpp:145
#define a
property_handler_map & property_handlers()
Definition: component.cpp:125
child_itors child_range(config_key_type key)
Definition: config.cpp:344
wfl::candidate_action_ptr ca_ptr
Definition: ai.cpp:74
virtual std::string get_id() const
Definition: stage.cpp:76
candidate_action_evaluation_loop(ai_context &context, const config &cfg)
Definition: stage_rca.cpp:42
std::vector< candidate_action_ptr > candidate_actions_
Definition: stage_rca.hpp:47
AI Support engine - creating specific ai components from config.
static void register_vector_property(property_handler_map &property_handlers, const std::string &property, std::vector< std::shared_ptr< X >> &values, std::function< void(std::vector< std::shared_ptr< X >> &, const config &)> construction_factory)
A helper class to observe the game state.
#define LOG_AI_TESTING_RCA_DEFAULT
Definition: stage_rca.cpp:39
#define b
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
Composite AI with turn sequence which is a vector of stages.
std::string path
Definition: game_config.cpp:39
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
void modify_active_ai_for_side(ai::side_number side, const config &cfg)
Modifies AI parameters for active AI of the given side.
Definition: manager.cpp:665
candidate action evaluator
std::size_t i
Definition: function.cpp:967
std::size_t index(const std::string &str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:72
static int sort(lua_State *L)
Definition: ltablib.cpp:397
virtual config to_config() const
serialize
Definition: stage.cpp:67
config & add_child(config_key_type key)
Definition: config.cpp:514
Composite AI component.
bool do_play_stage()
Play the turn - implementation.
Definition: stage_rca.cpp:82
Standard logging facilities (interface).
virtual side_number get_side() const =0
Get the side number.
static const double BAD_SCORE
Definition: rca.hpp:33
#define DBG_AI_TESTING_RCA_DEFAULT
Definition: stage_rca.cpp:38
static void parse_candidate_action_from_config(rca_context &context, const config &cfg, std::back_insert_iterator< std::vector< candidate_action_ptr >> b)
Definition: engine.cpp:61
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
bool operator()(const candidate_action_ptr &a, const candidate_action_ptr &b) const
Definition: stage_rca.cpp:76
candidate action framework