The Battle for Wesnoth  1.15.3+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 https://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 #include "ai/composite/ai.hpp"
22 #include "ai/composite/goal.hpp"
23 #include "ai/composite/rca.hpp"
24 #include "ai/composite/stage.hpp"
25 #include "ai/composite/aspect.hpp"
26 
28 
29 #include "log.hpp"
30 #include "resources.hpp"
31 #include "ai/lua/core.hpp"
32 #include "ai/lua/lua_object.hpp"
33 #include "game_board.hpp"
35 #include "units/unit.hpp"
36 #include "units/map.hpp"
37 
38 
39 namespace ai {
40 
41 static lg::log_domain log_ai_engine_lua("ai/engine/lua");
42 #define DBG_AI_LUA LOG_STREAM(debug, log_ai_engine_lua)
43 #define LOG_AI_LUA LOG_STREAM(info, log_ai_engine_lua)
44 #define WRN_AI_LUA LOG_STREAM(warn, log_ai_engine_lua)
45 #define ERR_AI_LUA LOG_STREAM(err, log_ai_engine_lua)
46 
47 #ifdef _MSC_VER
48 #pragma warning(push)
49 //silence "inherits via dominance" warnings
50 #pragma warning(disable:4250)
51 #endif
52 
53 typedef std::shared_ptr< lua_object<int> > lua_int_obj;
54 
56 
57 public:
59  : candidate_action(context, cfg),evaluation_action_handler_(),execution_action_handler_(),serialized_evaluation_state_(cfg.child_or_empty("args")),serialized_filterown_(cfg.child_or_empty("filter_own"))
60  {
61  // do nothing
62  }
63 
65 
66  virtual double evaluate()
67  {
68  lua_int_obj l_obj(new lua_object<int>());
69 
72  } else {
73  return BAD_SCORE;
74  }
75 
76  std::shared_ptr<int> result = l_obj->get();
77 
78  return result ? *result : 0.0;
79  }
80 
81 
82  virtual void execute() {
84  lua_object_ptr nil;
86  }
87  }
88 
89  virtual config to_config() const {
92  cfg.add_child("filter_own",serialized_filterown_);
93  return cfg;
94  }
95 
96 protected:
97  std::shared_ptr<lua_ai_action_handler> evaluation_action_handler_;
98  std::shared_ptr<lua_ai_action_handler> execution_action_handler_;
101 };
102 
104 
105 public:
106  lua_candidate_action_wrapper( rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
107  : lua_candidate_action_wrapper_base(context,cfg),evaluation_(cfg["evaluation"]),execution_(cfg["execution"])
108  {
109  evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(evaluation_.c_str(),lua_ai_ctx));
110  execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(execution_.c_str(),lua_ai_ctx));
111  }
112 
114 
115  virtual config to_config() const
116  {
118  cfg["evaluation"] = evaluation_;
119  cfg["execution"] = execution_;
120  return cfg;
121  }
122 
123 private:
124  std::string evaluation_;
125  std::string execution_;
126 };
127 
129 public:
131  : lua_candidate_action_wrapper_base(context,cfg), location_(cfg["location"]), use_parms_(false)
132  {
133  if (cfg.has_attribute("exec_parms") || cfg.has_attribute("eval_parms")) {
134  use_parms_ = true;
135  exec_parms_ = cfg["exec_parms"].str();
136  eval_parms_ = cfg["eval_parms"].str();
137  }
138  std::string eval_code;
139  std::string exec_code;
140  generate_code(eval_code, exec_code);
141 
142  evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(eval_code.c_str(),lua_ai_ctx));
143  execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(exec_code.c_str(),lua_ai_ctx));
144  }
145 
147 
148  virtual config to_config() const
149  {
151  cfg["location"] = location_;
152  if (use_parms_) {
153  cfg["eval_parms"] = eval_parms_;
154  cfg["exec_parms"] = exec_parms_;
155  }
156  return cfg;
157  }
158 
159 private:
160  std::string location_;
161  std::string eval_parms_;
162  std::string exec_parms_;
164 
165  void generate_code(std::string& eval, std::string& exec) {
166  std::string preamble = "local self, params, data, filter_own = ...\n";
167  std::string load = "wesnoth.require(\"" + location_ + "\")";
168  if (use_parms_) {
169  eval = preamble + "return " + load + ":evaluation(ai, {" + eval_parms_ + "}, {data = data})";
170  exec = preamble + load + ":execution(ai, {" + exec_parms_ + "}, {data = data})";
171  } else {
172  eval = preamble + "return " + load + ".evaluation(self, params, data, filter_own)";
173  exec = preamble + load + ".execution(self, params, data, filter_own)";
174  }
175  }
176 };
177 
179 public:
181  : lua_candidate_action_wrapper(context, cfg, lua_ai_ctx)
182  , bound_unit_()
183  {
184  map_location loc(cfg["unit_x"], cfg["unit_y"], wml_loc()); // lua and c++ coords differ by one
185  bound_unit_ = (*resources::gameboard->units().find(loc)).clone();
186  }
187 
188  virtual double evaluate()
189  {
190  if (resources::gameboard->units().find(bound_unit_->underlying_id()).valid())
191  {
193  }
194  else
195  {
196  this->set_to_be_removed();
197  return 0; // Is 0 what we return when we don't want the action to be executed?
198  }
199  }
200 
201  virtual void execute()
202  {
204  this->disable(); // we do not want to execute the same sticky CA twice -> will be moved out to Lua later
205  }
206 private:
208 
209 };
210 
211 class lua_stage_wrapper : public stage {
212 public:
213  lua_stage_wrapper( ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx )
214  : stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("args"))
215  {
216  action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx));
217  }
218 
220  {
221  }
222 
223  virtual bool do_play_stage()
224  {
225  gamestate_observer gs_o;
226 
227  if (action_handler_) {
228  lua_object_ptr nil;
229  const config empty_cfg;
230  action_handler_->handle(serialized_evaluation_state_, empty_cfg, false, nil);
231  }
232 
233  return gs_o.is_gamestate_changed();
234  }
235 
236  virtual config to_config() const
237  {
238  config cfg = stage::to_config();
239  cfg["code"] = code_;
241  return cfg;
242  }
243 private:
244  std::shared_ptr<lua_ai_action_handler> action_handler_;
245  std::string code_;
247 };
248 
249 
250 /**
251  * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack.
252  * We inject full ai_context later.
253  */
255  : engine(context,cfg)
256  , code_(get_engine_code(cfg))
257  , lua_ai_context_(resources::lua_kernel->create_lua_ai_context(
258  get_engine_code(cfg).c_str(), this))
259 {
260  name_ = "lua";
261  config data(cfg.child_or_empty("data"));
262  config args(cfg.child_or_empty("args"));
263 
264  if (lua_ai_context_) { // The context might be nullptr if the config contains errors
265  lua_ai_context_->set_persistent_data(data);
266  lua_ai_context_->set_arguments(args);
267  lua_ai_context_->update_state();
268  }
269 }
270 
271 std::string engine_lua::get_engine_code(const config &cfg) const
272 {
273  if (cfg.has_attribute("code")) {
274  return cfg["code"].str();
275  }
276  // If there is no engine defined we create a dummy engine
277  std::string code = "wesnoth.require(\"ai/lua/dummy_engine_lua.lua\")";
278  return code;
279 }
280 
282 {
283 }
284 
285 bool engine_lua::is_ok() const
286 {
287  return lua_ai_context_ ? true : false;
288 }
289 
291 {
292  if (game_config::debug)
293  {
294  lua_ai_context_->push_ai_table();
295  }
296 }
297 
298 void engine_lua::do_parse_candidate_action_from_config( rca_context &context, const config &cfg, std::back_insert_iterator<std::vector< candidate_action_ptr > > b ){
299  if (!cfg) {
300  return;
301  }
302 
303  if (!lua_ai_context_) {
304  return;
305  }
306 
308  if (!cfg["sticky"].to_bool())
309  {
310  if (cfg.has_attribute("location")) {
311  ca_ptr.reset(new lua_candidate_action_wrapper_external(context,cfg,*lua_ai_context_));
312  } else {
313  ca_ptr.reset(new lua_candidate_action_wrapper(context,cfg,*lua_ai_context_));
314  }
315  }
316  else
317  {
318  ca_ptr.reset(new lua_sticky_candidate_action_wrapper(context,cfg,*lua_ai_context_));
319  }
320 
321  if (ca_ptr) {
322  *b = ca_ptr;
323  }
324 }
325 
326 void engine_lua::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b )
327 {
328  if (!cfg) {
329  return;
330  }
331 
332  if (!lua_ai_context_) {
333  return;
334  }
335 
336  stage_ptr st_ptr(new lua_stage_wrapper(context,cfg,*lua_ai_context_));
337  if (st_ptr) {
338  st_ptr->on_create();
339  *b = st_ptr;
340  }
341 }
342 
343 void engine_lua::do_parse_aspect_from_config( const config &cfg, const std::string &id, std::back_insert_iterator<std::vector< aspect_ptr > > b )
344 {
345  const std::string aspect_factory_key = id+"*lua_aspect"; // @note: factory key for a lua_aspect
347 
348  if (f == lua_aspect_factory::get_list().end()){
349  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN aspect["<<aspect_factory_key<<"]" << std::endl;
350  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
351  return;
352  }
353  aspect_ptr new_aspect = f->second->get_new_instance(ai_,cfg,id,lua_ai_context_);
354  if (!new_aspect) {
355  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE aspect, key=["<<aspect_factory_key<<"]"<< std::endl;
356  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
357  return;
358  }
359  *b = new_aspect;
360 }
361 
362 void engine_lua::do_parse_goal_from_config(const config &cfg, std::back_insert_iterator<std::vector< goal_ptr > > b )
363 {
365  if (f == goal_factory::get_list().end()){
366  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN goal["<<cfg["name"]<<"]"<< std::endl;
367  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
368  return;
369  }
370  goal_ptr new_goal = f->second->get_new_instance(ai_,cfg);
371  new_goal->on_create(lua_ai_context_);
372  if (!new_goal || !new_goal->ok()) {
373  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE goal["<<cfg["name"]<<"]"<< std::endl;
374  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
375  return;
376  }
377  *b = new_goal;
378 }
379 
380 
381 std::string engine_lua::evaluate(const std::string &/*str*/)
382 {
383  // TODO: this is not mandatory, but if we want to allow lua to evaluate
384  // something 'in context' of this ai, this will be useful
385  return "";
386 }
387 
389 {
390  config cfg = engine::to_config();
391 
392  cfg["id"] = get_id();
393  cfg["code"] = this->code_;
394 
395  if (lua_ai_context_) {
396  config data = config();
397  lua_ai_context_->get_persistent_data(data);
398  cfg.add_child("data") = data;
399  }
400 
401  return cfg;
402 }
403 
404 #ifdef _MSC_VER
405 #pragma warning(pop)
406 #endif
407 
408 } //end of namespace ai
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:66
virtual void execute()
Execute the candidate action.
Definition: engine_lua.cpp:201
std::shared_ptr< candidate_action > candidate_action_ptr
Definition: rca.hpp:157
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:213
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:525
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:115
bool has_attribute(config_key_type key) const
Definition: config.cpp:213
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:388
virtual ~engine_lua()
Definition: engine_lua.cpp:281
Composite AI stages.
std::string get_engine_code(const config &) const
Definition: engine_lua.cpp:271
void generate_code(std::string &eval, std::string &exec)
Definition: engine_lua.cpp:165
virtual ~lua_stage_wrapper()
Definition: engine_lua.cpp:219
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:254
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:98
#define b
std::shared_ptr< lua_ai_action_handler > action_handler_
Definition: engine_lua.cpp:244
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< 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:362
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:82
config serialized_evaluation_state_
Definition: engine_lua.cpp:246
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:343
virtual void set_to_be_removed()
Definition: rca.cpp:123
#define DBG_AI_LUA
Definition: engine_lua.cpp:42
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:326
std::shared_ptr< lua_object< int > > lua_int_obj
Definition: engine_lua.cpp:53
Encapsulates the map of the game.
Definition: location.hpp:42
#define ERR_AI_LUA
Definition: engine_lua.cpp:45
unit_iterator find(std::size_t id)
Definition: map.cpp:311
virtual bool do_play_stage()
Play the turn - implementation.
Definition: engine_lua.cpp:223
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:188
virtual config to_config() const
serialize
Definition: engine_lua.cpp:89
bool is_ok() const
Definition: engine_lua.cpp:285
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:290
virtual config to_config() const
serialize
Definition: rca.cpp:107
virtual config to_config() const
serialize
Definition: engine_lua.cpp:236
std::shared_ptr< lua_ai_action_handler > evaluation_action_handler_
Definition: engine_lua.cpp:97
lua_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:106
const bool & debug
virtual config to_config() const
serialize
Definition: stage.cpp:66
config & add_child(config_key_type key)
Definition: config.cpp:476
void disable()
Disable the candidate action.
Definition: rca.cpp:71
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:180
#define f
bool find(E event, F functor)
Tests whether an event handler is available.
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:32
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:298
lua_candidate_action_wrapper_base(rca_context &context, const config &cfg)
Definition: engine_lua.cpp:58
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:453
virtual std::string evaluate(const std::string &str)
Definition: engine_lua.cpp:381
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:130
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:148