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