The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
configuration.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2016 by Yurii Chernyi <[email protected]>
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 /**
17  * Managing the AI configuration
18  * @file
19  */
20 
21 #include "ai/configuration.hpp"
22 
23 #include "filesystem.hpp"
24 #include "log.hpp"
25 #include "serialization/parser.hpp"
27 #include "wml_exception.hpp"
28 #include "config_assign.hpp"
29 
30 #include <vector>
31 #include <deque>
32 #include <set>
33 
34 namespace ai {
35 
36 static lg::log_domain log_ai_configuration("ai/config");
37 #define DBG_AI_CONFIGURATION LOG_STREAM(debug, log_ai_configuration)
38 #define LOG_AI_CONFIGURATION LOG_STREAM(info, log_ai_configuration)
39 #define WRN_AI_CONFIGURATION LOG_STREAM(warn, log_ai_configuration)
40 #define ERR_AI_CONFIGURATION LOG_STREAM(err, log_ai_configuration)
41 
43 {
44  ai_configurations_.clear();
45  era_ai_configurations_.clear();
46  mod_ai_configurations_.clear();
47 
48  const config &ais = game_config.child("ais");
49  default_config_ = ais.child("default_config");
50  if (!default_config_) {
51  ERR_AI_CONFIGURATION << "Missing AI [default_config]. Therefore, default_config_ set to empty." << std::endl;
53  }
54 
55 
56  for (const config &ai_configuration : ais.child_range("ai")) {
57  const std::string &id = ai_configuration["id"];
58  if (id.empty()){
59 
60  ERR_AI_CONFIGURATION << "skipped AI config due to missing id" << ". Config contains:"<< std::endl << ai_configuration << std::endl;
61  continue;
62  }
63  if (ai_configurations_.count(id)>0){
64  ERR_AI_CONFIGURATION << "skipped AI config due to duplicate id [" << id << "]. Config contains:"<< std::endl << ai_configuration << std::endl;
65  continue;
66  }
67 
68  description desc;
69  desc.id=id;
70  desc.text = ai_configuration["description"].t_str();
71  desc.cfg=ai_configuration;
72 
73  ai_configurations_.insert(std::make_pair(id,desc));
74  LOG_AI_CONFIGURATION << "loaded AI config: " << ai_configuration["description"] << std::endl;
75  }
76 }
77 
78 namespace {
79 void extract_ai_configurations(std::map<std::string, description> &storage, const config &input)
80 {
81  for (const config &ai_configuration : input.child_range("ai")) {
82  const std::string &id = ai_configuration["id"];
83  if (id.empty()){
84 
85  ERR_AI_CONFIGURATION << "skipped AI config due to missing id" << ". Config contains:"<< std::endl << ai_configuration << std::endl;
86  continue;
87  }
88  if (storage.count(id)>0){
89  ERR_AI_CONFIGURATION << "skipped AI config due to duplicate id [" << id << "]. Config contains:"<< std::endl << ai_configuration << std::endl;
90  continue;
91  }
92 
93  description desc;
94  desc.id=id;
95  desc.text = ai_configuration["description"].t_str();
96  desc.cfg=ai_configuration;
97 
98  storage.insert(std::make_pair(id,desc));
99  LOG_AI_CONFIGURATION << "loaded AI config: " << ai_configuration["description"] << std::endl;
100  }
101 }
102 }
103 
105 {
106  era_ai_configurations_.clear();
107  extract_ai_configurations(era_ai_configurations_, era);
108 }
109 
111 {
112  mod_ai_configurations_.clear();
113  for (const config &mod : mods) {
114  extract_ai_configurations(mod_ai_configurations_, mod);
115  }
116 }
117 
118 std::vector<description*> configuration::get_available_ais(){
119  std::vector<description*> ais_list;
120  for(description_map::iterator desc = ai_configurations_.begin(); desc!=ai_configurations_.end(); ++desc) {
121  ais_list.push_back(&desc->second);
122  DBG_AI_CONFIGURATION << "has ai with config: "<< std::endl << desc->second.cfg<< std::endl;
123  }
124  for(description_map::iterator desc = era_ai_configurations_.begin(); desc!=era_ai_configurations_.end(); ++desc) {
125  ais_list.push_back(&desc->second);
126  DBG_AI_CONFIGURATION << "has ai with config: "<< std::endl << desc->second.cfg<< std::endl;
127  }
128  for(description_map::iterator desc = mod_ai_configurations_.begin(); desc!=mod_ai_configurations_.end(); ++desc) {
129  ais_list.push_back(&desc->second);
130  DBG_AI_CONFIGURATION << "has ai with config: "<< std::endl << desc->second.cfg<< std::endl;
131  }
132  return ais_list;
133 }
134 
136 {
138  if (cfg_it==ai_configurations_.end()){
140  if (era_cfg_it==era_ai_configurations_.end()){
142  if (mod_cfg_it==mod_ai_configurations_.end()) {
143  return default_config_;
144  } else {
145  return mod_cfg_it->second.cfg;
146  }
147  } else {
148  return era_cfg_it->second.cfg;
149  }
150  }
151  return cfg_it->second.cfg;
152 }
153 
154 
159 
161  try {
163  read(cfg, *stream);
164  LOG_AI_CONFIGURATION << "Reading AI configuration from file '" << file << "'" << std::endl;
165  } catch(config::error &) {
166  ERR_AI_CONFIGURATION << "Error while reading AI configuration from file '" << file << "'" << std::endl;
167  return false;
168  }
169  LOG_AI_CONFIGURATION << "Successfully read AI configuration from file '" << file << "'" << std::endl;
170  return true;
171 }
172 
174 {
175  return default_config_;
176 }
177 
178 
179 bool configuration::parse_side_config(side_number side, const config& original_cfg, config &cfg )
180 {
181  LOG_AI_CONFIGURATION << "side "<< side <<": parsing AI configuration from config" << std::endl;
182 
183  //leave only the [ai] children
184  cfg = config();
185  for (const config &aiparam : original_cfg.child_range("ai")) {
186  cfg.add_child("ai",aiparam);
187  }
188 
189  //backward-compatibility hack: put ai_algorithm if it is present
190  if (const config::attribute_value *v = original_cfg.get("ai_algorithm")) {
191  config ai_a;
192  ai_a["ai_algorithm"] = *v;
193  cfg.add_child("ai",ai_a);
194  }
195  DBG_AI_CONFIGURATION << "side " << side << ": config contains:"<< std::endl << cfg << std::endl;
196 
197  //insert default config at the beginning
198  if (default_config_) {
199  DBG_AI_CONFIGURATION << "side "<< side <<": applying default configuration" << std::endl;
200  cfg.add_child_at("ai",default_config_,0);
201  } else {
202  ERR_AI_CONFIGURATION << "side "<< side <<": default configuration is not available, not applying it" << std::endl;
203  }
204 
205  LOG_AI_CONFIGURATION << "side "<< side << ": expanding simplified aspects into full facets"<< std::endl;
206  expand_simplified_aspects(side, cfg);
207 
208  //construct new-style integrated config
209  LOG_AI_CONFIGURATION << "side "<< side << ": doing final operations on AI config"<< std::endl;
210  config parsed_cfg = config();
211 
212  LOG_AI_CONFIGURATION << "side "<< side <<": merging AI configurations"<< std::endl;
213  for (const config &aiparam : cfg.child_range("ai")) {
214  parsed_cfg.append(aiparam);
215  }
216 
217 
218  LOG_AI_CONFIGURATION << "side "<< side <<": merging AI aspect with the same id"<< std::endl;
219  parsed_cfg.merge_children_by_attribute("aspect","id");
220 
221  LOG_AI_CONFIGURATION << "side "<< side <<": removing duplicate [default] tags from aspects"<< std::endl;
222  for (config &aspect_cfg : parsed_cfg.child_range("aspect")) {
223  if (aspect_cfg["name"] != "composite_aspect") {
224  // No point in warning about Lua or standard aspects lacking [default]
225  continue;
226  }
227  if (!aspect_cfg.child("default")) {
228  WRN_AI_CONFIGURATION << "side "<< side <<": aspect with id=["<<aspect_cfg["id"]<<"] lacks default config facet!" <<std::endl;
229  continue;
230  }
231  aspect_cfg.merge_children("default");
232  config& dflt = aspect_cfg.child("default");
233  if (dflt.has_child("value")) {
234  while (dflt.child_count("value") > 1) {
235  dflt.remove_child("value", 0);
236  }
237  }
238  }
239 
240  DBG_AI_CONFIGURATION << "side "<< side <<": done parsing side config, it contains:"<< std::endl << parsed_cfg << std::endl;
241  LOG_AI_CONFIGURATION << "side "<< side <<": done parsing side config"<< std::endl;
242 
243  cfg = parsed_cfg;
244  return true;
245 
246 }
247 
248 static const std::set<std::string> non_aspect_attributes = {"turns", "time_of_day", "engine", "ai_algorithm", "id", "description"};
249 static const std::set<std::string> just_copy_tags = {"engine", "stage", "aspect", "goal", "modify_ai"};
250 static const std::set<std::string> old_goal_tags = {"target", "target_location", "protect_unit", "protect_location"};
251 
253  std::string algorithm;
254  config base_config, parsed_config;
255  for (const config &aiparam : cfg.child_range("ai")) {
257  if (aiparam.has_attribute("turns")) {
258  turns = aiparam["turns"].str();
259  }
260  if (aiparam.has_attribute("time_of_day")) {
261  time_of_day = aiparam["time_of_day"].str();
262  }
263  if (aiparam.has_attribute("engine")) {
264  engine = aiparam["engine"].str();
265  }
266  if (aiparam.has_attribute("ai_algorithm")) {
267  if (algorithm.empty()) {
268  algorithm = aiparam["ai_algorithm"].str();
269  base_config = get_ai_config_for(algorithm);
270  } else if(algorithm != aiparam["ai_algorithm"]) {
271  lg::wml_error() << "side " << side << " has two [ai] tags with contradictory ai_algorithm - the first one will take precedence.\n";
272  }
273  }
274  std::deque<std::pair<std::string, config> > facet_configs;
275  for (const config::attribute &attr : aiparam.attribute_range()) {
276  if (non_aspect_attributes.count(attr.first)) {
277  continue;
278  }
279  config facet_config;
280  facet_config["engine"] = engine;
281  facet_config["name"] = "standard_aspect";
282  facet_config["turns"] = turns;
283  facet_config["time_of_day"] = time_of_day;
284  facet_config["value"] = attr.second;
285  facet_configs.push_back(std::make_pair(attr.first, facet_config));
286  }
287  for (const config::any_child &child : aiparam.all_children_range()) {
288  if (just_copy_tags.count(child.key)) {
289  // These aren't simplified, so just copy over unchanged.
290  parsed_config.add_child(child.key, child.cfg);
291  continue;
292  } else if(old_goal_tags.count(child.key)) {
293  // A simplified goal, mainly kept around just for backwards compatibility.
294  config goal_config, criteria_config = child.cfg;
295  goal_config["name"] = child.key;
296  goal_config["turns"] = turns;
297  goal_config["time_of_day"] = time_of_day;
298  if(child.key.substr(0,7) == "protect" && criteria_config.has_attribute("protect_radius")) {
299  goal_config["protect_radius"] = criteria_config["protect_radius"];
300  criteria_config.remove_attribute("protect_radius");
301  }
302  if(criteria_config.has_attribute("value")) {
303  goal_config["value"] = criteria_config["value"];
304  criteria_config.remove_attribute("value");
305  }
306  parsed_config.add_child("goal", goal_config);
307  continue;
308  }
309  // Now there's two possibilities. If the tag is [attacks] or contains either value= or [value],
310  // then it can be copied verbatim as a [facet] tag.
311  // Otherwise, it needs to be placed as a [value] within a [facet] tag.
312  if (child.key == "attacks" || child.cfg.has_attribute("value") || child.cfg.has_child("value")) {
313  facet_configs.push_back(std::make_pair(child.key, child.cfg));
314  } else {
315  config facet_config;
316  facet_config["engine"] = engine;
317  facet_config["name"] = "standard_aspect";
318  facet_config["turns"] = turns;
319  facet_config["time_of_day"] = time_of_day;
320  facet_config.add_child("value", child.cfg);
321  if (child.key == "leader_goal" && !child.cfg["id"].empty()) {
322  // Use id= attribute (if present) as the facet ID
323  const std::string& id = child.cfg["id"];
324  if(id != "*" && id.find_first_not_of("0123456789") != std::string::npos) {
325  facet_config["id"] = child.cfg["id"];
326  }
327  }
328  facet_configs.push_back(std::make_pair(child.key, facet_config));
329  }
330  }
331  std::map<std::string, config> aspect_configs;
332  while (!facet_configs.empty()) {
333  const std::string &aspect = facet_configs.front().first;
334  const config &facet_config = facet_configs.front().second;
335  aspect_configs[aspect]["id"] = aspect; // Will sometimes be redundant assignment
336  aspect_configs[aspect]["name"] = "composite_aspect";
337  aspect_configs[aspect].add_child("facet", facet_config);
338  facet_configs.pop_front();
339  }
340  typedef std::map<std::string, config>::value_type aspect_pair;
341  for (const aspect_pair& p : aspect_configs) {
342  parsed_config.add_child("aspect", p.second);
343  }
344  }
345  if (algorithm.empty() && !parsed_config.has_child("stage")) {
346  base_config = get_ai_config_for("ai_default_rca");
347  }
348  for (const config::any_child &child : parsed_config.all_children_range()) {
349  base_config.add_child(child.key, child.cfg);
350  }
351  cfg.clear_children("ai");
352  cfg.add_child("ai", base_config);
353 }
354 
355 } //end of namespace ai
static const std::set< std::string > non_aspect_attributes
child_itors child_range(const std::string &key)
Definition: config.cpp:613
void remove_attribute(const std::string &key)
Definition: config.cpp:534
std::string era()
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:566
AI parameters.
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
static config default_config_
GLenum GLenum GLenum input
Definition: glew.h:10668
attribute_map::value_type attribute
Definition: config.hpp:393
static void add_mod_ai_from_config(config::const_child_itors configs)
static bool parse_side_config(side_number side, const config &cfg, config &parsed_cfg)
#define WRN_AI_CONFIGURATION
void clear_children(const std::string &key)
Definition: config.cpp:820
std::pair< const_child_iterator, const_child_iterator > const_child_itors
Definition: config.hpp:214
Variant for storing WML attributes.
Definition: config.hpp:223
std::string id
static lg::log_domain log_ai_configuration("ai/config")
GLuint GLuint stream
Definition: glew.h:5239
static const std::set< std::string > just_copy_tags
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:48
bool has_child(const std::string &key) const
Determine whether a config has a child or not.
Definition: config.cpp:651
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:57
GLuint id
Definition: glew.h:1647
static description_map era_ai_configurations_
static std::vector< description * > get_available_ais()
Returns a list of available AIs.
#define LOG_AI_CONFIGURATION
const GLdouble * v
Definition: glew.h:1359
all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:1127
static description_map ai_configurations_
void remove_child(const std::string &key, unsigned index)
Definition: config.cpp:899
config & add_child(const std::string &key)
Definition: config.cpp:743
static void expand_simplified_aspects(side_number side, config &cfg)
Expand simplified aspects, similar to the change from 1.7.2 to 1.7.3 but with some additional syntax ...
static const std::set< std::string > old_goal_tags
GLfloat GLfloat p
Definition: glew.h:12766
#define DBG_AI_CONFIGURATION
std::stringstream & wml_error()
Use this logger to send errors due to deprecated WML.
Definition: log.cpp:262
std::string get_wml_location(const std::string &filename, const std::string &current_dir=std::string())
Returns a complete path to the actual WML file or directory or an empty string if the file isn't pres...
static void add_era_ai_from_config(const config &game_config)
Game configuration data as global variables.
Definition: build_info.cpp:38
std::map< std::string, description > description_map
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
static description_map mod_ai_configurations_
unsigned child_count(const std::string &key) const
Definition: config.cpp:635
const attribute_value * get(const std::string &key) const
Returns a pointer to the attribute with the given key or nullptr if it does not exist.
Definition: config.cpp:935
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
bool has_attribute(const std::string &key) const
Definition: config.cpp:514
static const config & get_default_ai_parameters()
get default AI parameters
#define ERR_AI_CONFIGURATION
Managing the AIs configuration - headers.
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
Standard logging facilities (interface).
static bool get_side_config_from_file(const std::string &file, config &cfg)
get side config from file
int side_number
Definition: game_info.hpp:44
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
static void init(const config &game_config)
Init the parameters of ai configuration parser.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
static const config & get_ai_config_for(const std::string &id)
Return the config for a specified ai.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
config & add_child_at(const std::string &key, const config &val, unsigned index)
Definition: config.cpp:773
void merge_children_by_attribute(const std::string &key, const std::string &attribute)
All children with the given key and with equal values of the specified attribute will be merged into ...
Definition: config.cpp:589