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