The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
engine_lua.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2017 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 #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:
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  return cfg;
93  }
94 
95 protected:
96  std::shared_ptr<lua_ai_action_handler> evaluation_action_handler_;
97  std::shared_ptr<lua_ai_action_handler> execution_action_handler_;
99 };
100 
102 
103 public:
104  lua_candidate_action_wrapper( rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
105  : lua_candidate_action_wrapper_base(context,cfg),evaluation_(cfg["evaluation"]),execution_(cfg["execution"])
106  {
107  evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(evaluation_.c_str(),lua_ai_ctx));
108  execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(execution_.c_str(),lua_ai_ctx));
109  }
110 
112 
113  virtual config to_config() const
114  {
116  cfg["evaluation"] = evaluation_;
117  cfg["execution"] = execution_;
118  return cfg;
119  }
120 
121 private:
124 };
125 
127 public:
129  : lua_candidate_action_wrapper_base(context,cfg), location_(cfg["location"]), use_parms_(false)
130  {
131  if (cfg.has_attribute("exec_parms") || cfg.has_attribute("eval_parms")) {
132  use_parms_ = true;
133  exec_parms_ = cfg["exec_parms"].str();
134  eval_parms_ = cfg["eval_parms"].str();
135  }
136  std::string eval_code;
137  std::string exec_code;
138  generate_code(eval_code, exec_code);
139 
140  evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(eval_code.c_str(),lua_ai_ctx));
141  execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(exec_code.c_str(),lua_ai_ctx));
142  }
143 
145 
146  virtual config to_config() const
147  {
149  cfg["location"] = location_;
150  if (use_parms_) {
151  cfg["eval_parms"] = eval_parms_;
152  cfg["exec_parms"] = exec_parms_;
153  }
154  return cfg;
155  }
156 
157 private:
162 
163  void generate_code(std::string& eval, std::string& exec) {
164  std::string preamble = "local self, params, data = ...\n";
165  std::string load = "wesnoth.require(\"" + location_ + "\")";
166  if (use_parms_) {
167  eval = preamble + "return " + load + ":evaluation(ai, {" + eval_parms_ + "}, {data = data})";
168  exec = preamble + load + ":execution(ai, {" + exec_parms_ + "}, {data = data})";
169  } else {
170  eval = preamble + "return " + load + ".evaluation(self, params, data)";
171  exec = preamble + load + ".execution(self, params, data)";
172  }
173  }
174 };
175 
177 public:
179  : lua_candidate_action_wrapper(context, cfg, lua_ai_ctx)
180  , bound_unit_()
181  {
182  map_location loc(cfg["unit_x"], cfg["unit_y"], wml_loc()); // lua and c++ coords differ by one
183  bound_unit_.reset(new unit(*resources::gameboard->units().find(loc)));
184  }
185 
186  virtual double evaluate()
187  {
188  if (resources::gameboard->units().find(bound_unit_->underlying_id()).valid())
189  {
191  }
192  else
193  {
194  this->set_to_be_removed();
195  return 0; // Is 0 what we return when we don't want the action to be executed?
196  }
197  }
198 
199  virtual void execute()
200  {
202  this->disable(); // we do not want to execute the same sticky CA twice -> will be moved out to Lua later
203  }
204 private:
206 
207 };
208 
209 class lua_stage_wrapper : public stage {
210 public:
211  lua_stage_wrapper( ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx )
212  : stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("args"))
213  {
214  action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx));
215  }
216 
218  {
219  }
220 
221  virtual bool do_play_stage()
222  {
223  gamestate_observer gs_o;
224 
225  if (action_handler_) {
226  lua_object_ptr nil;
227  action_handler_->handle(serialized_evaluation_state_, false, nil);
228  }
229 
230  return gs_o.is_gamestate_changed();
231  }
232 
233  virtual config to_config() const
234  {
235  config cfg = stage::to_config();
236  cfg["code"] = code_;
238  return cfg;
239  }
240 private:
241  std::shared_ptr<lua_ai_action_handler> action_handler_;
244 };
245 
246 
247 /**
248  * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack.
249  * We inject full ai_context later.
250  */
252  : engine(context,cfg)
253  , code_(get_engine_code(cfg))
254  , lua_ai_context_(resources::lua_kernel->create_lua_ai_context(
255  get_engine_code(cfg).c_str(), this))
256 {
257  name_ = "lua";
258  config data(cfg.child_or_empty("data"));
259  config args(cfg.child_or_empty("args"));
260 
261  if (lua_ai_context_) { // The context might be nullptr if the config contains errors
262  lua_ai_context_->set_persistent_data(data);
263  lua_ai_context_->set_arguments(args);
264  lua_ai_context_->update_state();
265  }
266 }
267 
269 {
270  if (cfg.has_attribute("code")) {
271  return cfg["code"].str();
272  }
273  // If there is no engine defined we create a dummy engine
274  std::string code = "wesnoth.require(\"ai/lua/dummy_engine_lua.lua\")";
275  return code;
276 }
277 
279 {
280 }
281 
282 bool engine_lua::is_ok() const
283 {
284  return lua_ai_context_ ? true : false;
285 }
286 
288 {
289  if (game_config::debug)
290  {
291  lua_ai_context_->push_ai_table();
292  }
293 }
294 
295 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 ){
296  if (!cfg) {
297  return;
298  }
299 
300  if (!lua_ai_context_) {
301  return;
302  }
303 
305  if (!cfg["sticky"].to_bool())
306  {
307  if (cfg.has_attribute("location")) {
308  ca_ptr.reset(new lua_candidate_action_wrapper_external(context,cfg,*lua_ai_context_));
309  } else {
310  ca_ptr.reset(new lua_candidate_action_wrapper(context,cfg,*lua_ai_context_));
311  }
312  }
313  else
314  {
315  ca_ptr.reset(new lua_sticky_candidate_action_wrapper(context,cfg,*lua_ai_context_));
316  }
317 
318  if (ca_ptr) {
319  *b = ca_ptr;
320  }
321 }
322 
323 void engine_lua::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b )
324 {
325  if (!cfg) {
326  return;
327  }
328 
329  if (!lua_ai_context_) {
330  return;
331  }
332 
333  stage_ptr st_ptr(new lua_stage_wrapper(context,cfg,*lua_ai_context_));
334  if (st_ptr) {
335  st_ptr->on_create();
336  *b = st_ptr;
337  }
338 }
339 
340 void engine_lua::do_parse_aspect_from_config( const config &cfg, const std::string &id, std::back_insert_iterator<std::vector< aspect_ptr > > b )
341 {
342  const std::string aspect_factory_key = id+"*lua_aspect"; // @note: factory key for a lua_aspect
344 
345  if (f == lua_aspect_factory::get_list().end()){
346  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN aspect["<<aspect_factory_key<<"]" << std::endl;
347  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
348  return;
349  }
350  aspect_ptr new_aspect = f->second->get_new_instance(ai_,cfg,id,lua_ai_context_);
351  if (!new_aspect) {
352  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE aspect, key=["<<aspect_factory_key<<"]"<< std::endl;
353  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
354  return;
355  }
356  *b = new_aspect;
357 }
358 
359 void engine_lua::do_parse_goal_from_config(const config &cfg, std::back_insert_iterator<std::vector< goal_ptr > > b )
360 {
362  if (f == goal_factory::get_list().end()){
363  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN goal["<<cfg["name"]<<"]"<< std::endl;
364  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
365  return;
366  }
367  goal_ptr new_goal = f->second->get_new_instance(ai_,cfg);
368  new_goal->on_create(lua_ai_context_);
369  if (!new_goal || !new_goal->ok()) {
370  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE goal["<<cfg["name"]<<"]"<< std::endl;
371  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
372  return;
373  }
374  *b = new_goal;
375 }
376 
377 
379 {
380  ///@todo this is not mandatory, but if we want to allow lua to evaluate
381  // something 'in context' of this ai, this will be useful
382  return "";
383 }
384 
386 {
387  config cfg = engine::to_config();
388 
389  cfg["id"] = get_id();
390  cfg["code"] = this->code_;
391 
392  if (lua_ai_context_) {
393  config data = config();
394  lua_ai_context_->get_persistent_data(data);
395  cfg.add_child("data") = data;
396  }
397 
398  return cfg;
399 }
400 
401 #ifdef _MSC_VER
402 #pragma warning(pop)
403 #endif
404 
405 } //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:199
std::shared_ptr< candidate_action > candidate_action_ptr
Definition: rca.hpp:147
std::shared_ptr< lua_ai_context > lua_ai_context_
Definition: engine_lua.hpp:87
std::vector< char_t > string
lua_stage_wrapper(ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:211
This class represents a single unit of a specific type.
Definition: unit.hpp:101
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
wfl::candidate_action_ptr ca_ptr
Definition: ai.cpp:73
static factory_map & get_list()
Definition: goal.hpp:183
virtual ~engine_lua()
Definition: engine_lua.cpp:278
Composite AI stages.
std::shared_ptr< aspect > aspect_ptr
Definition: game_info.hpp:105
void generate_code(std::string &eval, std::string &exec)
Definition: engine_lua.cpp:163
virtual ~lua_stage_wrapper()
Definition: engine_lua.cpp:217
std::string get_engine_code(const config &) const
Definition: engine_lua.cpp:268
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:251
virtual config to_config() const
Serialize to config.
Definition: engine_lua.cpp:385
std::shared_ptr< lua_ai_action_handler > execution_action_handler_
Definition: engine_lua.cpp:97
#define b
std::shared_ptr< lua_ai_action_handler > action_handler_
Definition: engine_lua.cpp:241
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
virtual config to_config() const
serialize
Definition: stage.cpp:66
std::shared_ptr< stage > stage_ptr
Definition: game_info.hpp:110
virtual config to_config() const
serialize
Definition: engine.cpp:138
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:359
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
virtual config to_config() const
serialize
Definition: rca.cpp:88
config serialized_evaluation_state_
Definition: engine_lua.cpp:243
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:340
virtual void set_to_be_removed()
Definition: rca.cpp:101
virtual config to_config() const
serialize
Definition: engine_lua.cpp:233
#define DBG_AI_LUA
Definition: engine_lua.cpp:42
std::shared_ptr< goal > goal_ptr
Definition: game_info.hpp:108
static lg::log_domain log_ai_engine_lua("ai/engine/lua")
virtual std::string get_id() const
Definition: engine.hpp:95
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:323
std::shared_ptr< lua_object< int > > lua_int_obj
Definition: engine_lua.cpp:53
virtual config to_config() const
serialize
Definition: engine_lua.cpp:146
Encapsulates the map of the game.
Definition: location.hpp:40
#define ERR_AI_LUA
Definition: engine_lua.cpp:45
bool has_attribute(config_key_type key) const
Definition: config.cpp:196
virtual bool do_play_stage()
Play the turn - implementation.
Definition: engine_lua.cpp:221
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:186
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:287
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:385
virtual config to_config() const
serialize
Definition: engine_lua.cpp:113
std::shared_ptr< lua_ai_action_handler > evaluation_action_handler_
Definition: engine_lua.cpp:96
lua_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:104
config & add_child(config_key_type key)
Definition: config.cpp:408
virtual config to_config() const
serialize
Definition: engine_lua.cpp:89
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:178
#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:37
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:295
lua_candidate_action_wrapper_base(rca_context &context, const config &cfg)
Definition: engine_lua.cpp:58
virtual std::string evaluate(const std::string &str)
Definition: engine_lua.cpp:378
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
LUA AI Support engine - creating specific ai components from config.
bool is_ok() const
Definition: engine_lua.cpp:282
lua_candidate_action_wrapper_external(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:128
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
unit_map * units
Definition: resources.cpp:34
readonly_context & ai_
Definition: engine.hpp:105
candidate action framework
std::shared_ptr< lua_object_base > lua_object_ptr
Definition: core.hpp:27