The Battle for Wesnoth  1.17.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 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 namespace ai {
39 
40 static lg::log_domain log_ai_engine_lua("ai/engine/lua");
41 #define DBG_AI_LUA LOG_STREAM(debug, log_ai_engine_lua)
42 #define LOG_AI_LUA LOG_STREAM(info, log_ai_engine_lua)
43 #define WRN_AI_LUA LOG_STREAM(warn, log_ai_engine_lua)
44 #define ERR_AI_LUA LOG_STREAM(err, log_ai_engine_lua)
45 
46 #ifdef _MSC_VER
47 #pragma warning(push)
48 //silence "inherits via dominance" warnings
49 #pragma warning(disable:4250)
50 #endif
51 
52 typedef std::shared_ptr< lua_object<int> > lua_int_obj;
53 
55 
56 public:
58  : 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"))
59  {
60  // do nothing
61  }
62 
64 
65  virtual double evaluate()
66  {
67  lua_int_obj l_obj(new lua_object<int>());
68 
71  } else {
72  return BAD_SCORE;
73  }
74 
75  std::shared_ptr<int> result = l_obj->get();
76 
77  return result ? *result : 0.0;
78  }
79 
80  virtual void execute() {
82  lua_object_ptr nil;
84  }
85  }
86 
87  virtual config to_config() const {
90  cfg.add_child("filter_own",serialized_filterown_);
91  return cfg;
92  }
93 
94 protected:
95  std::shared_ptr<lua_ai_action_handler> evaluation_action_handler_;
96  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:
122  std::string evaluation_;
123  std::string execution_;
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:
158  std::string location_;
159  std::string eval_parms_;
160  std::string exec_parms_;
162 
163  void generate_code(std::string& eval, std::string& exec) {
164  std::string preamble = "local self, params, data, filter_own = ...\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, filter_own)";
171  exec = preamble + load + ".execution(self, params, data, filter_own)";
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_ = (*resources::gameboard->units().find(loc)).clone();
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  const config empty_cfg;
228  action_handler_->handle(serialized_evaluation_state_, empty_cfg, false, nil);
229  }
230 
231  return gs_o.is_gamestate_changed();
232  }
233 
234  virtual config to_config() const
235  {
236  config cfg = stage::to_config();
237  cfg["code"] = code_;
239  return cfg;
240  }
241 private:
242  std::shared_ptr<lua_ai_action_handler> action_handler_;
243  std::string code_;
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 
268 std::string engine_lua::get_engine_code(const config &cfg) const
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 std::string engine_lua::evaluate(const std::string &/*str*/)
378 {
379  // TODO: this is not mandatory, but if we want to allow lua to evaluate
380  // something 'in context' of this ai, this will be useful
381  return "";
382 }
383 
385 {
386  config cfg = engine::to_config();
387 
388  cfg["id"] = get_id();
389  cfg["code"] = this->code_;
390 
391  if (lua_ai_context_) {
392  config data = config();
393  lua_ai_context_->get_persistent_data(data);
394  cfg.add_child("data") = data;
395  }
396 
397  return cfg;
398 }
399 
400 #ifdef _MSC_VER
401 #pragma warning(pop)
402 #endif
403 
404 } //end of namespace ai
virtual double evaluate()
Evaluate the candidate action, resetting the internal state of the action.
Definition: engine_lua.cpp:65
virtual void execute()
Execute the candidate action.
Definition: engine_lua.cpp:199
std::shared_ptr< candidate_action > candidate_action_ptr
Definition: rca.hpp:144
std::shared_ptr< lua_ai_context > lua_ai_context_
Definition: engine_lua.hpp:86
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:211
virtual const unit_map & units() const override
Definition: game_board.hpp:111
std::string code_
Method to inject AI context into the engine.
Definition: engine_lua.hpp:83
static factory_map & get_list()
Definition: aspect.hpp:499
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:113
bool has_attribute(config_key_type key) const
Definition: config.cpp:210
virtual config to_config() const
serialize
Definition: engine.cpp:133
wfl::candidate_action_ptr ca_ptr
Definition: ai.cpp:73
static factory_map & get_list()
Definition: goal.hpp:158
virtual config to_config() const
Serialize to config.
Definition: engine_lua.cpp:384
virtual ~engine_lua()
Definition: engine_lua.cpp:278
Composite AI stages.
std::string get_engine_code(const config &) const
Definition: engine_lua.cpp:268
void generate_code(std::string &eval, std::string &exec)
Definition: engine_lua.cpp:163
virtual ~lua_stage_wrapper()
Definition: engine_lua.cpp:217
virtual std::string get_id() const
Definition: engine.hpp:86
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:25
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
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:96
#define b
std::shared_ptr< lua_ai_action_handler > action_handler_
Definition: engine_lua.cpp:242
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:60
std::shared_ptr< goal > goal_ptr
Definition: game_info.hpp:99
Proxy table for the AI context.
Definition: core.hpp:32
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:80
config serialized_evaluation_state_
Definition: engine_lua.cpp:244
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:118
#define DBG_AI_LUA
Definition: engine_lua.cpp:41
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:323
std::shared_ptr< lua_object< int > > lua_int_obj
Definition: engine_lua.cpp:52
Encapsulates the map of the game.
Definition: location.hpp:37
#define ERR_AI_LUA
Definition: engine_lua.cpp:44
unit_iterator find(std::size_t id)
Definition: map.cpp:309
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 config to_config() const
serialize
Definition: engine_lua.cpp:87
bool is_ok() const
Definition: engine_lua.cpp:282
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
virtual config to_config() const
serialize
Definition: rca.cpp:102
virtual config to_config() const
serialize
Definition: engine_lua.cpp:234
std::shared_ptr< lua_ai_action_handler > evaluation_action_handler_
Definition: engine_lua.cpp:95
lua_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:104
const bool & debug
virtual config to_config() const
serialize
Definition: stage.cpp:66
config & add_child(config_key_type key)
Definition: config.cpp:504
void disable()
Disable the candidate action.
Definition: rca.cpp:69
lua_sticky_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:178
#define f
std::string name_
Definition: engine.hpp:102
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:295
lua_candidate_action_wrapper_base(rca_context &context, const config &cfg)
Definition: engine_lua.cpp:57
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:481
virtual std::string evaluate(const std::string &str)
Definition: engine_lua.cpp:377
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
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:128
std::string::const_iterator iterator
Definition: tokenizer.hpp:24
readonly_context & ai_
Definition: engine.hpp:96
candidate action framework
std::shared_ptr< lua_object_base > lua_object_ptr
Definition: core.hpp:26
virtual config to_config() const
serialize
Definition: engine_lua.cpp:146