The Battle for Wesnoth  1.15.0-dev
engine_lua.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2018 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  * LUA AI Support engine - creating specific ai components from config
17  * @file
18  */
19 
20 #include "ai/lua/engine_lua.hpp"
21 
22 #include "ai/composite/ai.hpp"
23 #include "ai/composite/aspect.hpp"
24 #include "ai/composite/goal.hpp"
25 #include "ai/composite/rca.hpp"
26 #include "ai/composite/stage.hpp"
28 #include "ai/lua/core.hpp"
29 #include "ai/lua/lua_object.hpp"
30 #include "game_board.hpp"
31 #include "log.hpp"
32 #include "resources.hpp"
34 #include "units/map.hpp"
35 #include "units/unit.hpp"
36 
37 namespace ai
38 {
39 static lg::log_domain log_ai_engine_lua("ai/engine/lua");
40 #define DBG_AI_LUA LOG_STREAM(debug, log_ai_engine_lua)
41 #define LOG_AI_LUA LOG_STREAM(info, log_ai_engine_lua)
42 #define WRN_AI_LUA LOG_STREAM(warn, log_ai_engine_lua)
43 #define ERR_AI_LUA LOG_STREAM(err, log_ai_engine_lua)
44 
45 #ifdef _MSC_VER
46 #pragma warning(push)
47 // silence "inherits via dominance" warnings
48 #pragma warning(disable : 4250)
49 #endif
50 
51 typedef std::shared_ptr<lua_object<int>> lua_int_obj;
52 
54 {
55 public:
57  : candidate_action(context, cfg)
60  , serialized_evaluation_state_(cfg.child_or_empty("args"))
61  {
62  // do nothing
63  }
64 
66  {
67  }
68 
69  virtual double evaluate()
70  {
71  lua_int_obj l_obj(new lua_object<int>());
72 
75  } else {
76  return BAD_SCORE;
77  }
78 
79  std::shared_ptr<int> result = l_obj->get();
80 
81  return result ? *result : 0.0;
82  }
83 
84  virtual void execute()
85  {
87  lua_object_ptr nil;
89  }
90  }
91 
92  virtual config to_config() const
93  {
96  return cfg;
97  }
98 
99 protected:
100  std::shared_ptr<lua_ai_action_handler> evaluation_action_handler_;
101  std::shared_ptr<lua_ai_action_handler> execution_action_handler_;
103 };
104 
106 {
107 public:
109  : lua_candidate_action_wrapper_base(context, cfg)
110  , evaluation_(cfg["evaluation"])
111  , execution_(cfg["execution"])
112  {
114  resources::lua_kernel->create_lua_ai_action_handler(evaluation_.c_str(), lua_ai_ctx));
116  resources::lua_kernel->create_lua_ai_action_handler(execution_.c_str(), lua_ai_ctx));
117  }
118 
120  {
121  }
122 
123  virtual config to_config() const
124  {
126  cfg["evaluation"] = evaluation_;
127  cfg["execution"] = execution_;
128  return cfg;
129  }
130 
131 private:
132  std::string evaluation_;
133  std::string execution_;
134 };
135 
137 {
138 public:
140  : lua_candidate_action_wrapper_base(context, cfg)
141  , location_(cfg["location"])
142  , use_parms_(false)
143  {
144  if(cfg.has_attribute("exec_parms") || cfg.has_attribute("eval_parms")) {
145  use_parms_ = true;
146  exec_parms_ = cfg["exec_parms"].str();
147  eval_parms_ = cfg["eval_parms"].str();
148  }
149  std::string eval_code;
150  std::string exec_code;
151  generate_code(eval_code, exec_code);
152 
154  resources::lua_kernel->create_lua_ai_action_handler(eval_code.c_str(), lua_ai_ctx));
156  resources::lua_kernel->create_lua_ai_action_handler(exec_code.c_str(), lua_ai_ctx));
157  }
158 
160  {
161  }
162 
163  virtual config to_config() const
164  {
166  cfg["location"] = location_;
167  if(use_parms_) {
168  cfg["eval_parms"] = eval_parms_;
169  cfg["exec_parms"] = exec_parms_;
170  }
171 
172  return cfg;
173  }
174 
175 private:
176  std::string location_;
177  std::string eval_parms_;
178  std::string exec_parms_;
180 
181  void generate_code(std::string& eval, std::string& exec)
182  {
183  std::string preamble = "local self, params, data = ...\n";
184  std::string load = "wesnoth.require(\"" + location_ + "\")";
185 
186  if(use_parms_) {
187  eval = preamble + "return " + load + ":evaluation(ai, {" + eval_parms_ + "}, {data = data})";
188  exec = preamble + load + ":execution(ai, {" + exec_parms_ + "}, {data = data})";
189  } else {
190  eval = preamble + "return " + load + ".evaluation(self, params, data)";
191  exec = preamble + load + ".execution(self, params, data)";
192  }
193  }
194 };
195 
197 {
198 public:
200  : lua_candidate_action_wrapper(context, cfg, lua_ai_ctx)
201  , bound_unit_()
202  {
203  map_location loc(cfg["unit_x"], cfg["unit_y"], wml_loc()); // lua and c++ coords differ by one
204  bound_unit_ = (*resources::gameboard->units().find(loc)).clone();
205  }
206 
207  virtual double evaluate()
208  {
209  if(resources::gameboard->units().find(bound_unit_->underlying_id()).valid()) {
211  } else {
212  this->set_to_be_removed();
213  return 0; // Is 0 what we return when we don't want the action to be executed?
214  }
215  }
216 
217  virtual void execute()
218  {
220  this->disable(); // we do not want to execute the same sticky CA twice -> will be moved out to Lua later
221  }
222 
223 private:
225 };
226 
227 class lua_stage_wrapper : public stage
228 {
229 public:
230  lua_stage_wrapper(ai_context& context, const config& cfg, lua_ai_context& lua_ai_ctx)
231  : stage(context, cfg)
232  , action_handler_()
233  , code_(cfg["code"])
234  , serialized_evaluation_state_(cfg.child_or_empty("args"))
235  {
236  action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(), lua_ai_ctx));
237  }
238 
240  {
241  }
242 
243  virtual bool do_play_stage()
244  {
245  gamestate_observer gs_o;
246 
247  if(action_handler_) {
248  lua_object_ptr nil;
249  action_handler_->handle(serialized_evaluation_state_, false, nil);
250  }
251 
252  return gs_o.is_gamestate_changed();
253  }
254 
255  virtual config to_config() const
256  {
257  config cfg = stage::to_config();
258  cfg["code"] = code_;
260  return cfg;
261  }
262 
263 private:
264  std::shared_ptr<lua_ai_action_handler> action_handler_;
265  std::string code_;
267 };
268 
269 /**
270  * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to
271  * move/attack. We inject full ai_context later.
272  */
274  : engine(context, cfg)
275  , code_(get_engine_code(cfg))
276  , lua_ai_context_(resources::lua_kernel->create_lua_ai_context(get_engine_code(cfg).c_str(), this))
277 {
278  name_ = "lua";
279  config data(cfg.child_or_empty("data"));
280  config args(cfg.child_or_empty("args"));
281 
282  if(lua_ai_context_) { // The context might be nullptr if the config contains errors
283  lua_ai_context_->set_persistent_data(data);
284  lua_ai_context_->set_arguments(args);
285  lua_ai_context_->update_state();
286  }
287 }
288 
289 std::string engine_lua::get_engine_code(const config& cfg) const
290 {
291  if(cfg.has_attribute("code")) {
292  return cfg["code"].str();
293  }
294  // If there is no engine defined we create a dummy engine
295  std::string code = "wesnoth.require(\"ai/lua/dummy_engine_lua.lua\")";
296  return code;
297 }
298 
300 {
301 }
302 
303 bool engine_lua::is_ok() const
304 {
305  return lua_ai_context_ ? true : false;
306 }
307 
309 {
310  if(game_config::debug) {
311  lua_ai_context_->push_ai_table();
312  }
313 }
314 
316  rca_context& context, const config& cfg, std::back_insert_iterator<std::vector<candidate_action_ptr>> b)
317 {
318  if(!cfg) {
319  return;
320  }
321 
322  if(!lua_ai_context_) {
323  return;
324  }
325 
327  if(!cfg["sticky"].to_bool()) {
328  if(cfg.has_attribute("location")) {
329  ca_ptr.reset(new lua_candidate_action_wrapper_external(context, cfg, *lua_ai_context_));
330  } else {
331  ca_ptr.reset(new lua_candidate_action_wrapper(context, cfg, *lua_ai_context_));
332  }
333  } else {
334  ca_ptr.reset(new lua_sticky_candidate_action_wrapper(context, cfg, *lua_ai_context_));
335  }
336 
337  if(ca_ptr) {
338  *b = ca_ptr;
339  }
340 }
341 
343  ai_context& context, const config& cfg, std::back_insert_iterator<std::vector<stage_ptr>> b)
344 {
345  if(!cfg) {
346  return;
347  }
348 
349  if(!lua_ai_context_) {
350  return;
351  }
352 
353  stage_ptr st_ptr(new lua_stage_wrapper(context, cfg, *lua_ai_context_));
354  if(st_ptr) {
355  st_ptr->on_create();
356  *b = st_ptr;
357  }
358 }
359 
361  const config& cfg, const std::string& id, std::back_insert_iterator<std::vector<aspect_ptr>> b)
362 {
363  const std::string aspect_factory_key = id + "*lua_aspect"; // @note: factory key for a lua_aspect
365 
366  if(f == lua_aspect_factory::get_list().end()) {
367  ERR_AI_LUA << "side " << ai_.get_side() << " : UNKNOWN aspect[" << aspect_factory_key << "]" << std::endl;
368  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
369  return;
370  }
371 
372  aspect_ptr new_aspect = f->second->get_new_instance(ai_, cfg, id, lua_ai_context_);
373  if(!new_aspect) {
374  ERR_AI_LUA << "side " << ai_.get_side() << " : UNABLE TO CREATE aspect, key=[" << aspect_factory_key << "]"
375  << std::endl;
376  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
377  return;
378  }
379 
380  *b = new_aspect;
381 }
382 
383 void engine_lua::do_parse_goal_from_config(const config& cfg, std::back_insert_iterator<std::vector<goal_ptr>> b)
384 {
386  if(f == goal_factory::get_list().end()) {
387  ERR_AI_LUA << "side " << ai_.get_side() << " : UNKNOWN goal[" << cfg["name"] << "]" << std::endl;
388  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
389  return;
390  }
391 
392  goal_ptr new_goal = f->second->get_new_instance(ai_, cfg);
393  new_goal->on_create(lua_ai_context_);
394  if(!new_goal || !new_goal->ok()) {
395  ERR_AI_LUA << "side " << ai_.get_side() << " : UNABLE TO CREATE goal[" << cfg["name"] << "]" << std::endl;
396  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
397  return;
398  }
399 
400  *b = new_goal;
401 }
402 
403 std::string engine_lua::evaluate(const std::string& /*str*/)
404 {
405  ///@todo this is not mandatory, but if we want to allow lua to evaluate
406  // something 'in context' of this ai, this will be useful
407  return "";
408 }
409 
411 {
412  config cfg = engine::to_config();
413 
414  cfg["id"] = get_id();
415  cfg["code"] = this->code_;
416 
417  if(lua_ai_context_) {
418  config data = config();
419  lua_ai_context_->get_persistent_data(data);
420  cfg.add_child("data") = data;
421  }
422 
423  return cfg;
424 }
425 
426 #ifdef _MSC_VER
427 #pragma warning(pop)
428 #endif
429 
430 } // end of namespace ai
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:69
virtual void execute()
Execute the candidate action.
Definition: engine_lua.cpp:217
std::shared_ptr< candidate_action > candidate_action_ptr
Definition: rca.hpp:141
std::shared_ptr< lua_ai_context > lua_ai_context_
Definition: engine_lua.hpp:87
std::shared_ptr< stage > stage_ptr
Definition: game_info.hpp:101
lua_stage_wrapper(ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:230
virtual const unit_map & units() const override
Definition: game_board.hpp:114
std::string code_
Method to inject AI context into the engine.
Definition: engine_lua.hpp:84
static factory_map & get_list()
Definition: aspect.hpp:516
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.
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:35
virtual config to_config() const
serialize
Definition: engine_lua.cpp:123
bool has_attribute(config_key_type key) const
Definition: config.cpp:217
virtual config to_config() const
serialize
Definition: engine.cpp:138
wfl::candidate_action_ptr ca_ptr
Definition: ai.cpp:73
static factory_map & get_list()
Definition: goal.hpp:177
virtual config to_config() const
Serialize to config.
Definition: engine_lua.cpp:410
virtual ~engine_lua()
Definition: engine_lua.cpp:299
Composite AI stages.
std::string get_engine_code(const config &) const
Definition: engine_lua.cpp:289
void generate_code(std::string &eval, std::string &exec)
Definition: engine_lua.cpp:181
virtual ~lua_stage_wrapper()
Definition: engine_lua.cpp:239
virtual std::string get_id() const
Definition: engine.hpp:95
A helper class to observe the game state.
Lua object(value) wrapper implementation.
engine_lua(readonly_context &context, const config &cfg)
Note that initially we get access only to readonly context (engine is created rather early...
Definition: engine_lua.cpp:273
std::shared_ptr< aspect > aspect_ptr
Definition: game_info.hpp:94
std::shared_ptr< lua_ai_action_handler > execution_action_handler_
Definition: engine_lua.cpp:101
#define b
std::shared_ptr< lua_ai_action_handler > action_handler_
Definition: engine_lua.cpp:264
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
std::shared_ptr< lua_object< int > > lua_int_obj
Definition: engine_lua.cpp:51
std::shared_ptr< goal > goal_ptr
Definition: game_info.hpp:99
Proxy table for the AI context.
Definition: core.hpp:33
virtual void do_parse_goal_from_config(const config &cfg, std::back_insert_iterator< std::vector< goal_ptr > > b)
Definition: engine_lua.cpp:383
game_board * gameboard
Definition: resources.cpp:20
Composite AI with turn sequence which is a vector of stages.
virtual void execute()
Execute the candidate action.
Definition: engine_lua.cpp:84
config serialized_evaluation_state_
Definition: engine_lua.cpp:266
virtual void do_parse_aspect_from_config(const config &cfg, const std::string &id, std::back_insert_iterator< std::vector< aspect_ptr > > b)
Taka a config (with engine=lua in it) and parse several (usually, 1) aspects out of it...
Definition: engine_lua.cpp:360
virtual void set_to_be_removed()
Definition: rca.cpp:101
#define DBG_AI_LUA
Definition: engine_lua.cpp:40
static lg::log_domain log_ai_engine_lua("ai/engine/lua")
virtual void do_parse_stage_from_config(ai_context &context, const config &cfg, std::back_insert_iterator< std::vector< stage_ptr > > b)
Taka a config (with engine=lua in it) and parse several (usually, 1) stages out of it...
Definition: engine_lua.cpp:342
Encapsulates the map of the game.
Definition: location.hpp:42
#define ERR_AI_LUA
Definition: engine_lua.cpp:43
unit_iterator find(std::size_t id)
Definition: map.cpp:311
virtual bool do_play_stage()
Play the turn - implementation.
Definition: engine_lua.cpp:243
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:207
virtual config to_config() const
serialize
Definition: engine_lua.cpp:92
bool is_ok() const
Definition: engine_lua.cpp:303
virtual void push_ai_table()
Method that pushes the AI table of the lua_context on the stack for debugging purposes.
Definition: engine_lua.cpp:308
virtual config to_config() const
serialize
Definition: rca.cpp:88
virtual config to_config() const
serialize
Definition: engine_lua.cpp:255
std::shared_ptr< lua_ai_action_handler > evaluation_action_handler_
Definition: engine_lua.cpp:100
lua_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:108
const bool & debug
virtual config to_config() const
serialize
Definition: stage.cpp:66
config & add_child(config_key_type key)
Definition: config.cpp:479
void disable()
Disable the candidate action.
Definition: rca.cpp:66
boost::intrusive_ptr< unit > unit_ptr
Definition: ptr.hpp:29
lua_sticky_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:199
#define f
std::string name_
Definition: engine.hpp:111
Standard logging facilities (interface).
virtual side_number get_side() const =0
Get the side number.
static const double BAD_SCORE
Definition: rca.hpp:31
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
virtual void do_parse_candidate_action_from_config(rca_context &context, const config &cfg, std::back_insert_iterator< std::vector< candidate_action_ptr > > b)
Taka a config (with engine=lua in it) and parse several (usually, 1) candidate actions out of it...
Definition: engine_lua.cpp:315
lua_candidate_action_wrapper_base(rca_context &context, const config &cfg)
Definition: engine_lua.cpp:56
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:456
virtual std::string evaluate(const std::string &str)
Definition: engine_lua.cpp:403
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
LUA AI Support engine - creating specific ai components from config.
lua_candidate_action_wrapper_external(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:139
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
readonly_context & ai_
Definition: engine.hpp:105
candidate action framework
std::shared_ptr< lua_object_base > lua_object_ptr
Definition: core.hpp:27
virtual config to_config() const
serialize
Definition: engine_lua.cpp:163