The Battle for Wesnoth  1.17.0-dev
engine_lua.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  * LUA AI Support engine - creating specific ai components from config
18  * @file
19  */
20 
21 #include "ai/lua/engine_lua.hpp"
22 #include "ai/composite/ai.hpp"
23 #include "ai/composite/goal.hpp"
24 #include "ai/composite/rca.hpp"
25 #include "ai/composite/stage.hpp"
26 #include "ai/composite/aspect.hpp"
27 
29 
30 #include "log.hpp"
31 #include "resources.hpp"
32 #include "ai/lua/core.hpp"
33 #include "ai/lua/lua_object.hpp"
34 #include "game_board.hpp"
36 #include "units/unit.hpp"
37 #include "units/map.hpp"
38 #include "deprecation.hpp"
39 #include "game_version.hpp"
40 
41 namespace ai {
42 
43 static lg::log_domain log_ai_engine_lua("ai/engine/lua");
44 #define DBG_AI_LUA LOG_STREAM(debug, log_ai_engine_lua)
45 #define LOG_AI_LUA LOG_STREAM(info, log_ai_engine_lua)
46 #define WRN_AI_LUA LOG_STREAM(warn, log_ai_engine_lua)
47 #define ERR_AI_LUA LOG_STREAM(err, log_ai_engine_lua)
48 
49 #ifdef _MSC_VER
50 #pragma warning(push)
51 //silence "inherits via dominance" warnings
52 #pragma warning(disable:4250)
53 #endif
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  auto l_obj = std::make_shared<lua_object<double>>();
69 
72  } else {
73  return BAD_SCORE;
74  }
75 
76  std::shared_ptr<double> result = l_obj->get();
77 
78  return result ? *result : 0.0;
79  }
80 
81  virtual void execute() {
83  lua_object_ptr nil;
85  }
86  }
87 
88  virtual config to_config() const {
91  cfg.add_child("filter_own",serialized_filterown_);
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_;
100 };
101 
103 
104 public:
105  lua_candidate_action_wrapper( rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
106  : lua_candidate_action_wrapper_base(context,cfg),evaluation_(cfg["evaluation"]),execution_(cfg["execution"])
107  {
108  evaluation_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(evaluation_.c_str(),lua_ai_ctx));
109  execution_action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(execution_.c_str(),lua_ai_ctx));
110  }
111 
113 
114  virtual config to_config() const
115  {
117  cfg["evaluation"] = evaluation_;
118  cfg["execution"] = execution_;
119  return cfg;
120  }
121 
122 private:
123  std::string evaluation_;
124  std::string execution_;
125 };
126 
128 public:
130  : lua_candidate_action_wrapper_base(context,cfg), location_(cfg["location"]), use_parms_(false)
131  {
132  if (cfg.has_attribute("exec_parms") || cfg.has_attribute("eval_parms")) {
133  deprecated_message("[candidate_action]eval_parms,exec_parms=", DEP_LEVEL::PREEMPTIVE, "1.17", "Use [args] instead - this data is passed to both the evaluation and the execution");
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 
207  virtual config to_config() const
208  {
210  cfg["sticky"] = true;
211  cfg["unit_x"] = bound_unit_->get_location().wml_x();
212  cfg["unit_y"] = bound_unit_->get_location().wml_y();
213  return cfg;
214  }
215 private:
217 
218 };
219 
220 class lua_stage_wrapper : public stage {
221 public:
222  lua_stage_wrapper( ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx )
223  : stage(context,cfg),action_handler_(),code_(cfg["code"]),serialized_evaluation_state_(cfg.child_or_empty("args"))
224  {
225  action_handler_.reset(resources::lua_kernel->create_lua_ai_action_handler(code_.c_str(),lua_ai_ctx));
226  }
227 
229  {
230  }
231 
232  virtual bool do_play_stage()
233  {
234  gamestate_observer gs_o;
235 
236  if (action_handler_) {
237  lua_object_ptr nil;
238  const config empty_cfg;
239  action_handler_->handle(serialized_evaluation_state_, empty_cfg, false, nil);
240  }
241 
242  return gs_o.is_gamestate_changed();
243  }
244 
245  virtual config to_config() const
246  {
247  config cfg = stage::to_config();
248  cfg["code"] = code_;
250  return cfg;
251  }
252 private:
253  std::shared_ptr<lua_ai_action_handler> action_handler_;
254  std::string code_;
256 };
257 
258 /**
259  * Note that initially we get access only to readonly context (engine is created rather early, when there's no way to move/attack.
260  * We inject full ai_context later.
261  */
263  : engine(context,cfg)
264  , code_(get_engine_code(cfg))
265  , lua_ai_context_(resources::lua_kernel->create_lua_ai_context(
266  get_engine_code(cfg).c_str(), this))
267 {
268  name_ = "lua";
269  config data(cfg.child_or_empty("data"));
270  config args(cfg.child_or_empty("args"));
271 
272  if (lua_ai_context_) { // The context might be nullptr if the config contains errors
273  lua_ai_context_->set_persistent_data(data);
274  lua_ai_context_->set_arguments(args);
275  lua_ai_context_->update_state();
276  }
277 }
278 
279 std::string engine_lua::get_engine_code(const config &cfg) const
280 {
281  if (cfg.has_attribute("code")) {
282  return cfg["code"].str();
283  }
284  // If there is no engine defined we create a dummy engine
285  std::string code = "wesnoth.require(\"ai/lua/dummy_engine_lua.lua\")";
286  return code;
287 }
288 
290 {
291 }
292 
293 bool engine_lua::is_ok() const
294 {
295  return lua_ai_context_ ? true : false;
296 }
297 
299 {
300  if (game_config::debug)
301  {
302  lua_ai_context_->push_ai_table();
303  }
304 }
305 
306 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 ){
307  if (!cfg) {
308  return;
309  }
310 
311  if (!lua_ai_context_) {
312  return;
313  }
314 
316  if (!cfg["sticky"].to_bool())
317  {
318  if (cfg.has_attribute("location")) {
319  ca_ptr.reset(new lua_candidate_action_wrapper_external(context,cfg,*lua_ai_context_));
320  } else {
321  ca_ptr.reset(new lua_candidate_action_wrapper(context,cfg,*lua_ai_context_));
322  }
323  }
324  else
325  {
326  ca_ptr.reset(new lua_sticky_candidate_action_wrapper(context,cfg,*lua_ai_context_));
327  }
328 
329  if (ca_ptr) {
330  *b = ca_ptr;
331  }
332 }
333 
334 void engine_lua::do_parse_stage_from_config( ai_context &context, const config &cfg, std::back_insert_iterator<std::vector< stage_ptr > > b )
335 {
336  if (!cfg) {
337  return;
338  }
339 
340  if (!lua_ai_context_) {
341  return;
342  }
343 
344  stage_ptr st_ptr(new lua_stage_wrapper(context,cfg,*lua_ai_context_));
345  if (st_ptr) {
346  st_ptr->on_create();
347  *b = st_ptr;
348  }
349 }
350 
351 void engine_lua::do_parse_aspect_from_config( const config &cfg, const std::string &id, std::back_insert_iterator<std::vector< aspect_ptr > > b )
352 {
353  const std::string aspect_factory_key = id+"*lua_aspect"; // @note: factory key for a lua_aspect
355 
356  if (f == lua_aspect_factory::get_list().end()){
357  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN aspect["<<aspect_factory_key<<"]" << std::endl;
358  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
359  return;
360  }
361  aspect_ptr new_aspect = f->second->get_new_instance(ai_,cfg,id,lua_ai_context_);
362  if (!new_aspect) {
363  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE aspect, key=["<<aspect_factory_key<<"]"<< std::endl;
364  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
365  return;
366  }
367  *b = new_aspect;
368 }
369 
370 void engine_lua::do_parse_goal_from_config(const config &cfg, std::back_insert_iterator<std::vector< goal_ptr > > b )
371 {
373  if (f == goal_factory::get_list().end()){
374  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNKNOWN goal["<<cfg["name"]<<"]"<< std::endl;
375  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
376  return;
377  }
378  goal_ptr new_goal = f->second->get_new_instance(ai_,cfg);
379  new_goal->on_create(lua_ai_context_);
380  if (!new_goal || !new_goal->ok()) {
381  ERR_AI_LUA << "side "<<ai_.get_side()<< " : UNABLE TO CREATE goal["<<cfg["name"]<<"]"<< std::endl;
382  DBG_AI_LUA << "config snippet contains: " << std::endl << cfg << std::endl;
383  return;
384  }
385  *b = new_goal;
386 }
387 
388 std::string engine_lua::evaluate(const std::string &/*str*/)
389 {
390  // TODO: this is not mandatory, but if we want to allow lua to evaluate
391  // something 'in context' of this ai, this will be useful
392  return "";
393 }
394 
396 {
397  lua_ai_context_->apply_micro_ai(cfg);
398 }
399 
401 {
402  config cfg = engine::to_config();
403 
404  cfg["id"] = get_id();
405  cfg["code"] = this->code_;
406 
407  if (lua_ai_context_) {
408  config data = config();
409  lua_ai_context_->get_persistent_data(data);
410  cfg.add_child("data") = data;
411  }
412 
413  return cfg;
414 }
415 
416 #ifdef _MSC_VER
417 #pragma warning(pop)
418 #endif
419 
420 } //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:145
std::shared_ptr< lua_ai_context > lua_ai_context_
Definition: engine_lua.hpp:89
std::shared_ptr< stage > stage_ptr
Definition: game_info.hpp:102
lua_stage_wrapper(ai_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:222
virtual const unit_map & units() const override
Definition: game_board.hpp:112
Interfaces for manipulating version numbers of engine, add-ons, etc.
std::string code_
Method to inject AI context into the engine.
Definition: engine_lua.hpp:86
static factory_map & get_list()
Definition: aspect.hpp:500
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:36
virtual config to_config() const
serialize
Definition: engine_lua.cpp:114
bool has_attribute(config_key_type key) const
Definition: config.cpp:211
virtual config to_config() const
serialize
Definition: engine.cpp:134
wfl::candidate_action_ptr ca_ptr
Definition: ai.cpp:74
static factory_map & get_list()
Definition: goal.hpp:159
virtual config to_config() const
Serialize to config.
Definition: engine_lua.cpp:400
virtual ~engine_lua()
Definition: engine_lua.cpp:289
Composite AI stages.
std::string get_engine_code(const config &) const
Definition: engine_lua.cpp:279
void generate_code(std::string &eval, std::string &exec)
Definition: engine_lua.cpp:165
virtual ~lua_stage_wrapper()
Definition: engine_lua.cpp:228
virtual std::string get_id() const
Definition: engine.hpp:86
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
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:262
std::shared_ptr< aspect > aspect_ptr
Definition: game_info.hpp:95
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:253
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
std::shared_ptr< goal > goal_ptr
Definition: game_info.hpp:100
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:30
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:370
game_board * gameboard
Definition: resources.cpp:21
Composite AI with turn sequence which is a vector of stages.
virtual void execute()
Execute the candidate action.
Definition: engine_lua.cpp:81
config serialized_evaluation_state_
Definition: engine_lua.cpp:255
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:351
virtual void set_to_be_removed()
Definition: rca.cpp:119
#define DBG_AI_LUA
Definition: engine_lua.cpp:44
virtual config to_config() const
serialize
Definition: engine_lua.cpp:207
void apply_micro_ai(const config &cfg)
Definition: engine_lua.cpp:395
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:334
Encapsulates the map of the game.
Definition: location.hpp:38
#define ERR_AI_LUA
Definition: engine_lua.cpp:47
unit_iterator find(std::size_t id)
Definition: map.cpp:310
virtual bool do_play_stage()
Play the turn - implementation.
Definition: engine_lua.cpp:232
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:88
bool is_ok() const
Definition: engine_lua.cpp:293
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:298
virtual config to_config() const
serialize
Definition: rca.cpp:103
virtual config to_config() const
serialize
Definition: engine_lua.cpp:245
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:105
const bool & debug
virtual config to_config() const
serialize
Definition: stage.cpp:67
config & add_child(config_key_type key)
Definition: config.cpp:514
void disable()
Disable the candidate action.
Definition: rca.cpp:70
lua_sticky_candidate_action_wrapper(rca_context &context, const config &cfg, lua_ai_context &lua_ai_ctx)
Definition: engine_lua.cpp:180
#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:33
game_lua_kernel * lua_kernel
Definition: resources.cpp:26
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:306
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:465
virtual std::string evaluate(const std::string &str)
Definition: engine_lua.cpp:388
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
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:129
std::string::const_iterator iterator
Definition: tokenizer.hpp:25
readonly_context & ai_
Definition: engine.hpp:96
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