The Battle for Wesnoth  1.19.7+dev
component.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
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  * Composite AI component
18  * @file
19  */
20 
23 #include "config.hpp"
24 #include "log.hpp"
25 
26 
27 #include <boost/regex.hpp>
28 
29 namespace ai {
30 
31 static lg::log_domain log_ai_component("ai/component");
32 #define DBG_AI_COMPONENT LOG_STREAM(debug, log_ai_component)
33 #define LOG_AI_COMPONENT LOG_STREAM(info, log_ai_component)
34 #define ERR_AI_COMPONENT LOG_STREAM(err, log_ai_component)
35 
36 /*
37 [modify_ai]
38  path = "stage[fallback]
39  action = "change"
40  [stage]...[/stage]
41 [/modify_ai]
42 
43 [modify_ai]
44  component = "aspect[avoid].facet[zzz]"
45  action = "change"
46  [facet]...[/facet]
47 [/modify_ai]
48 
49 [modify_ai]
50  path = "aspect[aggression].facet[zzzz]
51  action = "delete"
52 [/modify_ai]
53 
54 [modify_ai]
55  component = "aspect[aggression].facet"
56  action = "add"
57  [facet]...[/facet]
58 [/modify_ai]
59 */
60 
62 {
64  if (i!=property_handlers_.end()) {
65  return i->second->handle_get(child);
66  }
67  return nullptr;
68 }
69 
70 bool component::add_child(const path_element &child, const config &cfg)
71 {
73  if (i!=property_handlers_.end()) {
74  return i->second->handle_add(child,cfg);
75  }
76  return false;
77 }
78 
79 bool component::change_child(const path_element &child, const config &cfg)
80 {
82  if (i!=property_handlers_.end()) {
83  return i->second->handle_change(child,cfg);
84  }
85  return false;
86 }
87 
89 {
91  if (i!=property_handlers_.end()) {
92  return i->second->handle_delete(child);
93  }
94  return false;
95 }
96 
97 std::vector<component*> component::get_children(const std::string &type)
98 {
100  if (i!=property_handlers_.end()) {
101  return i->second->handle_get_children();
102  }
103 
104  return std::vector<component*>();
105 }
106 
107 std::vector<std::string> component::get_children_types()
108 {
109  std::vector<std::string> types;
110  for (property_handler_map::value_type &ph : property_handlers_) {
111  types.push_back(ph.first);
112  }
113  return types;
114 }
115 
117 {
118  return property_handlers_;
119 }
120 
121 static component *find_component(component *root, const std::string &path, path_element &tail)
122 {
123  if (root==nullptr) {
124  return nullptr;
125  }
126 
127  //match path elements in [modify_ai] tag
128  boost::regex re(R"(([^\.^\[]+)(\[(\d*)\]|\[([^\]]+)\]|()))");
129  const int sub_matches[] {1,3,4};
130  boost::sregex_token_iterator i(path.begin(), path.end(), re, sub_matches);
131  boost::sregex_token_iterator j;
132 
133  component *c = root;
134 
135  std::vector< path_element > elements;
136  while(i != j)
137  {
138  path_element pe;
139  pe.property = *i++;
140  std::string position = *i++;
141  pe.id = *i++;
142  if (position.empty()) {
143  pe.position = -2;
144  } else {
145  try {
146  pe.position = std::stoi(position);
147  } catch (const std::invalid_argument&) {
148  pe.position = -2;
149  }
150  }
151  //DBG_AI_COMPONENT << "adding path element: "<< pe;
152  elements.push_back(pe);
153  }
154  if (elements.size()<1) {
155  return nullptr;
156  }
157 
158  std::vector< path_element >::iterator k_max = elements.end()-1;
159  for (std::vector< path_element >::iterator k = elements.begin(); k!=k_max; ++k) {
160  //not last
161  c = c->get_child(*k);
162  if (c==nullptr) {
163  return nullptr;
164  }
165  }
166 
167  tail = *k_max;
168  return c;
169 
170 }
171 
172 bool component_manager::add_component(component *root, const std::string &path, const config &cfg)
173 {
174  path_element tail;
175  component *c = find_component(root,path,tail);
176  if (c==nullptr) {
177  return false;
178  }
179  auto ch = cfg.optional_child(tail.property);
180  if (!ch) {
181  return false;
182  }
183  return c->add_child(tail, *ch);
184 
185 }
186 
187 bool component_manager::change_component(component *root, const std::string &path, const config &cfg)
188 {
189  path_element tail;
190  component *c = find_component(root,path,tail);
191  if (c==nullptr) {
192  return false;
193  }
194  auto ch = cfg.optional_child(tail.property);
195  if (!ch) {
196  return false;
197  }
198  return c->change_child(tail, *ch);
199 }
200 
201 bool component_manager::delete_component(component *root, const std::string &path)
202 {
203  path_element tail;
204  component *c = find_component(root,path,tail);
205  if (c==nullptr) {
206  return false;
207  }
208  return c->delete_child(tail);
209 }
210 
211 static void print_component(component *root, const std::string &type, std::stringstream &s, int offset)
212 {
213  std::stringstream offset_ss;
214  for (int i=0;i<offset;++i) {
215  offset_ss<<" ";
216  }
217  const std::string &offset_str = offset_ss.str();
218 
219  const std::vector<std::string> &t_list = root->get_children_types();
220 
221  s << offset_str << type<<"["<<root->get_id() <<"] "<<root->get_engine()<<" "<<root->get_name()<< std::endl;
222 
223  for (std::string t : t_list) {
224  std::vector<component*> c_list = root->get_children(t);
225  for (component *c : c_list) {
226  print_component(c,t,s,offset+1);
227  }
228  }
229 }
230 
231 std::string component_manager::print_component_tree(component *root, const std::string &path)
232 {
233  path_element tail;
234  component *c;
235  if (!path.empty()) {
236  c = find_component(root,path,tail);
237  if (c==nullptr) {
238  ERR_AI_COMPONENT << "unable to find component";
239  return "";
240  }
241  } else {
242  c = root;
243  }
244  std::stringstream s;
245  print_component(c, "", s, 0);
246  return s.str();
247 }
248 
250 {
251  if(!path.empty()) {
252  path_element tail;
253  return find_component(root, path, tail);
254  }
255  return nullptr;
256 }
257 
258 } //end of namespace ai
259 
260 std::ostream &operator<<(std::ostream &o, const ai::path_element &e)
261 {
262  o << "property["<<e.property<<"] id["<<e.id <<"] position["<<e.position<<"]"<<std::endl;
263  return o;
264 }
double t
Definition: astarsearch.cpp:63
static bool delete_component(component *root, const std::string &path)
Definition: component.cpp:201
static component * get_component(component *root, const std::string &path)
Definition: component.cpp:249
static bool change_component(component *root, const std::string &path, const config &cfg)
Definition: component.cpp:187
static bool add_component(component *root, const std::string &path, const config &cfg)
Definition: component.cpp:172
static std::string print_component_tree(component *root, const std::string &path)
Definition: component.cpp:231
virtual bool delete_child(const path_element &child)
Definition: component.cpp:88
virtual std::string get_engine() const =0
virtual bool change_child(const path_element &child, const config &cfg)
Definition: component.cpp:79
virtual component * get_child(const path_element &child)
Definition: component.cpp:61
virtual std::string get_name() const =0
virtual bool add_child(const path_element &child, const config &cfg)
Definition: component.cpp:70
virtual std::vector< component * > get_children(const std::string &type)
Definition: component.cpp:97
virtual std::string get_id() const =0
property_handler_map & property_handlers()
Definition: component.cpp:116
virtual std::vector< std::string > get_children_types()
Definition: component.cpp:107
property_handler_map property_handlers_
Definition: component.hpp:71
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
optional_config_impl< config > optional_child(config_key_type key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
Definition: config.cpp:384
std::ostream & operator<<(std::ostream &o, const ai::path_element &e)
Definition: component.cpp:260
#define ERR_AI_COMPONENT
Definition: component.cpp:34
A component of the AI framework.
Definitions for the interface to Wesnoth Markup Language (WML).
std::size_t i
Definition: function.cpp:1029
Standard logging facilities (interface).
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
static void print_component(component *root, const std::string &type, std::stringstream &s, int offset)
Definition: component.cpp:211
static lg::log_domain log_ai_component("ai/component")
std::map< std::string, property_handler_ptr > property_handler_map
Definition: component.hpp:48
static component * find_component(component *root, const std::string &path, path_element &tail)
Definition: component.cpp:121
std::string path
Definition: filesystem.cpp:92
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
Definition: charconv.hpp:154
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
Composite AI component.
std::string property
Definition: component.hpp:41
std::string id
Definition: component.hpp:42
mock_char c
static map_location::direction s
#define e